Compare commits

...

2 Commits

Author SHA1 Message Date
bjorn 70e0f5c5cf Synchronization; 2022-06-11 22:55:43 -07:00
bjorn d9b5237851 Sync cleanup; 2022-06-11 19:07:46 -07:00
3 changed files with 212 additions and 111 deletions

View File

@ -495,10 +495,8 @@ typedef enum {
GPU_PHASE_SHADER_COMPUTE = (1 << 5),
GPU_PHASE_DEPTH_EARLY = (1 << 6),
GPU_PHASE_DEPTH_LATE = (1 << 7),
GPU_PHASE_BLEND = (1 << 8),
GPU_PHASE_COPY = (1 << 9),
GPU_PHASE_BLIT = (1 << 10),
GPU_PHASE_CLEAR = (1 << 11)
GPU_PHASE_COLOR = (1 << 8),
GPU_PHASE_TRANSFER = (1 << 9)
} gpu_phase;
typedef enum {
@ -511,12 +509,11 @@ typedef enum {
GPU_CACHE_STORAGE_WRITE = (1 << 6),
GPU_CACHE_DEPTH_READ = (1 << 7),
GPU_CACHE_DEPTH_WRITE = (1 << 8),
GPU_CACHE_BLEND_READ = (1 << 9),
GPU_CACHE_BLEND_WRITE = (1 << 10),
GPU_CACHE_COLOR_READ = (1 << 9),
GPU_CACHE_COLOR_WRITE = (1 << 10),
GPU_CACHE_TRANSFER_READ = (1 << 11),
GPU_CACHE_TRANSFER_WRITE = (1 << 12),
GPU_CACHE_ATTACHMENT = GPU_CACHE_DEPTH_READ | GPU_CACHE_DEPTH_WRITE | GPU_CACHE_BLEND_READ | GPU_CACHE_BLEND_WRITE,
GPU_CACHE_WRITE = GPU_CACHE_STORAGE_WRITE | GPU_CACHE_DEPTH_WRITE | GPU_CACHE_BLEND_WRITE | GPU_CACHE_TRANSFER_WRITE,
GPU_CACHE_WRITE = GPU_CACHE_STORAGE_WRITE | GPU_CACHE_DEPTH_WRITE | GPU_CACHE_COLOR_WRITE | GPU_CACHE_TRANSFER_WRITE,
GPU_CACHE_READ = ~GPU_CACHE_WRITE
} gpu_cache;
@ -524,7 +521,7 @@ typedef struct {
gpu_phase prev;
gpu_phase next;
gpu_cache flush;
gpu_cache invalidate;
gpu_cache clear;
} gpu_barrier;
gpu_stream* gpu_stream_begin(const char* label);

View File

@ -1619,7 +1619,7 @@ void gpu_sync(gpu_stream* stream, gpu_barrier* barriers, uint32_t count) {
src |= convertPhase(barrier->prev);
dst |= convertPhase(barrier->next);
memoryBarrier.srcAccessMask |= convertCache(barrier->flush);
memoryBarrier.dstAccessMask |= convertCache(barrier->invalidate);
memoryBarrier.dstAccessMask |= convertCache(barrier->clear);
}
if (src && dst) {
@ -2643,10 +2643,8 @@ static VkPipelineStageFlags convertPhase(gpu_phase phase) {
if (phase & GPU_PHASE_SHADER_COMPUTE) flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
if (phase & GPU_PHASE_DEPTH_EARLY) flags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
if (phase & GPU_PHASE_DEPTH_LATE) flags |= VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
if (phase & GPU_PHASE_BLEND) flags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
if (phase & GPU_PHASE_COPY) flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
if (phase & GPU_PHASE_BLIT) flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
if (phase & GPU_PHASE_CLEAR) flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
if (phase & GPU_PHASE_COLOR) flags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
if (phase & GPU_PHASE_TRANSFER) flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
return flags;
}
@ -2661,8 +2659,8 @@ static VkAccessFlags convertCache(gpu_cache cache) {
if (cache & GPU_CACHE_STORAGE_WRITE) flags |= VK_ACCESS_SHADER_WRITE_BIT;
if (cache & GPU_CACHE_DEPTH_READ) flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
if (cache & GPU_CACHE_DEPTH_WRITE) flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
if (cache & GPU_CACHE_BLEND_READ) flags |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
if (cache & GPU_CACHE_BLEND_WRITE) flags |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
if (cache & GPU_CACHE_COLOR_READ) flags |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
if (cache & GPU_CACHE_COLOR_WRITE) flags |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
if (cache & GPU_CACHE_TRANSFER_READ) flags |= VK_ACCESS_TRANSFER_READ_BIT;
if (cache & GPU_CACHE_TRANSFER_WRITE) flags |= VK_ACCESS_TRANSFER_WRITE_BIT;
return flags;

View File

@ -33,7 +33,7 @@ typedef struct {
gpu_phase readPhase;
gpu_phase writePhase;
gpu_cache pendingReads;
gpu_cache pendingWrites;
gpu_cache pendingWrite;
uint32_t lastWriteIndex;
} Sync;
@ -123,6 +123,39 @@ typedef struct {
float color[4];
} DrawData;
typedef enum {
VERTEX_SHAPE,
VERTEX_POINT,
VERTEX_MODEL,
VERTEX_GLYPH,
VERTEX_EMPTY,
DEFAULT_FORMAT_COUNT
} DefaultFormat;
typedef struct {
MeshMode mode;
DefaultShader shader;
float* transform;
struct {
Buffer* buffer;
DefaultFormat format;
const void* data;
void** pointer;
uint32_t count;
} vertex;
struct {
Buffer* buffer;
const void* data;
void** pointer;
uint32_t count;
uint32_t stride;
} index;
uint32_t start;
uint32_t count;
uint32_t instances;
uint32_t base;
} Draw;
typedef struct {
Sync* sync;
Buffer* buffer;
@ -158,45 +191,12 @@ struct Pass {
arr_t(Access) access;
};
typedef enum {
VERTEX_SHAPE,
VERTEX_POINT,
VERTEX_MODEL,
VERTEX_GLYPH,
VERTEX_EMPTY,
DEFAULT_FORMAT_COUNT
} DefaultFormat;
typedef struct {
struct { float x, y, z; } position;
struct { float x, y, z; } normal;
struct { float u, v; } uv;
} ShapeVertex;
typedef struct {
MeshMode mode;
DefaultShader shader;
float* transform;
struct {
Buffer* buffer;
DefaultFormat format;
const void* data;
void** pointer;
uint32_t count;
} vertex;
struct {
Buffer* buffer;
const void* data;
void** pointer;
uint32_t count;
uint32_t stride;
} index;
uint32_t start;
uint32_t count;
uint32_t instances;
uint32_t base;
} Draw;
typedef struct {
void* next;
gpu_bundle_pool* gpu;
@ -228,7 +228,8 @@ static struct {
bool initialized;
bool active;
uint32_t tick;
Pass* uploads;
gpu_stream* stream;
bool hasTextureUpload;
gpu_device_info device;
gpu_features features;
gpu_limits limits;
@ -252,7 +253,6 @@ static struct {
static void* tempAlloc(size_t size);
static void* tempGrow(void* p, size_t size);
static void beginFrame(void);
static gpu_stream* getUploads(void);
static uint32_t getLayout(gpu_slot* slots, uint32_t count);
static gpu_bundle* getBundle(uint32_t layout);
static gpu_texture* getAttachment(uint32_t size[2], uint32_t layers, TextureFormat format, bool srgb, uint32_t samples);
@ -359,8 +359,8 @@ bool lovrGraphicsInit(bool debug, bool vsync) {
});
}
gpu_stream* uploads = getUploads();
gpu_clear_buffer(uploads, state.defaultBuffer->gpu, 0, 4096);
beginFrame();
gpu_clear_buffer(state.stream, state.defaultBuffer->gpu, 0, 4096);
state.vertexFormats[VERTEX_SHAPE].gpu = (gpu_vertex_format) {
.bufferCount = 2,
@ -520,32 +520,27 @@ void lovrGraphicsSubmit(Pass** passes, uint32_t count) {
return;
}
if (state.window) {
state.window->gpu = NULL;
state.window->renderView = NULL;
}
// Allocate a few extra stream handles for any internal passes we sneak in
gpu_stream** streams = tempAlloc((count + 3) * sizeof(gpu_stream*));
uint32_t extraPassCount = 0;
if (state.uploads) {
gpu_barrier barrier;
barrier.prev = GPU_PHASE_COPY;
barrier.next = ~0u;
barrier.flush = GPU_CACHE_TRANSFER_WRITE;
barrier.invalidate = ~0u;
gpu_sync(state.uploads->stream, &barrier, 1);
streams[extraPassCount++] = state.uploads->stream;
uint32_t total = count + 1;
gpu_stream** streams = tempAlloc(total * sizeof(gpu_stream*));
gpu_barrier* barriers = tempAlloc(count * sizeof(gpu_barrier));
memset(barriers, 0, count * sizeof(gpu_barrier));
streams[0] = state.stream;
if (state.hasTextureUpload) {
barriers[0].prev |= GPU_PHASE_TRANSFER;
barriers[0].next |= GPU_PHASE_SHADER_VERTEX;
barriers[0].next |= GPU_PHASE_SHADER_FRAGMENT;
barriers[0].next |= GPU_PHASE_SHADER_COMPUTE;
barriers[0].flush |= GPU_CACHE_TRANSFER_WRITE;
barriers[0].clear |= GPU_CACHE_TEXTURE;
state.hasTextureUpload = false;
}
// End passes
for (uint32_t i = 0; i < count; i++) {
for (uint32_t j = 0; j <= passes[i]->pipelineIndex; j++) {
lovrRelease(passes[i]->pipelines[j].shader, lovrShaderDestroy);
passes[i]->pipelines[j].shader = NULL;
}
streams[extraPassCount + i] = passes[i]->stream;
streams[i + 1] = passes[i]->stream;
if (passes[i]->info.type == PASS_RENDER) {
gpu_render_end(passes[i]->stream);
} else if (passes[i]->info.type == PASS_COMPUTE) {
@ -553,13 +548,112 @@ void lovrGraphicsSubmit(Pass** passes, uint32_t count) {
}
}
for (uint32_t i = 0; i < extraPassCount + count; i++) {
// Synchronization
for (uint32_t i = 0; i < count; i++) {
Pass* pass = passes[i];
for (uint32_t j = 0; j < pass->access.length; j++) {
// access is the incoming resource access performed by the pass
Access* access = &pass->access.data[j];
// sync is the existing state of the resource at the time of the access
Sync* sync = access->sync;
// barrier is a barrier that will be emitted at the end of the last pass that wrote to the
// resource (or the first pass if the last write was in a previous frame). The barrier
// specifies 'phases' and 'caches'. The 'next' phase will wait for the 'prev' phase to
// finish. In between, the 'flush' cache is flushed and the 'clear' cache is cleared.
gpu_barrier* barrier = &barriers[sync->lastWriteIndex];
// Only the first write in a pass is considered, because each pass can only perform one type
// of write to a resource, and there is not currently any intra-pass synchronization.
if (sync->lastWriteIndex == i + 1) {
continue;
}
// There are 4 types of access patterns:
// - read after read:
// - no hazard, no barrier necessary
// - read after write:
// - needs execution dependency to ensure the read happens after the write
// - needs to flush the writes from the cache
// - needs to clear the cache for the read so it gets the new data
// - only needs to happen once for each type of read after a write (tracked by pendingReads)
// - if a second read happens, the first read would have already synchronized (transitive)
// - write after write:
// - needs execution dependency to ensure writes don't overlap
// - needs to flush and clear the cache
// - resets the 'last write' and clears pendingReads
// - write after read:
// - needs execution dependency to ensure write starts after read is finished
// - does not need to flush any caches
// - does clear the write cache
// - resets the 'last write' and clears pendingReads
uint32_t read = access->cache & GPU_CACHE_READ;
uint32_t write = access->cache & GPU_CACHE_WRITE;
uint32_t newReads = read & ~sync->pendingReads;
bool hasNewReads = newReads || (access->phase & ~sync->readPhase);
bool readAfterWrite = read && sync->pendingWrite && hasNewReads;
bool writeAfterWrite = write && sync->pendingWrite && !sync->pendingReads;
bool writeAfterRead = write && sync->pendingReads;
if (readAfterWrite) {
barrier->prev |= sync->writePhase;
barrier->next |= access->phase;
barrier->flush |= sync->pendingWrite;
barrier->clear |= newReads;
sync->readPhase |= access->phase;
sync->pendingReads |= read;
}
if (writeAfterWrite) {
barrier->prev |= sync->writePhase;
barrier->next |= access->phase;
barrier->flush |= sync->pendingWrite;
barrier->clear |= write;
}
if (writeAfterRead) {
barrier->prev |= sync->readPhase;
barrier->next |= access->phase;
sync->readPhase = 0;
sync->pendingReads = 0;
}
if (write) {
sync->writePhase = access->phase;
sync->pendingWrite = write;
sync->lastWriteIndex = i + 1;
}
lovrRelease(access->buffer, lovrBufferDestroy);
lovrRelease(access->texture, lovrTextureDestroy);
}
}
for (uint32_t i = 0; i < count; i++) {
for (uint32_t j = 0; j < passes[i]->access.length; j++) {
passes[i]->access.data[j].sync->lastWriteIndex = 0;
}
}
for (uint32_t i = 0; i < count; i++) {
gpu_sync(streams[i], &barriers[i], 1);
}
for (uint32_t i = 0; i < total; i++) {
gpu_stream_end(streams[i]);
}
gpu_submit(streams, extraPassCount + count);
gpu_submit(streams, total);
state.uploads = NULL;
if (state.window) {
state.window->gpu = NULL;
state.window->renderView = NULL;
}
state.stream = NULL;
state.active = false;
}
@ -629,10 +723,12 @@ Buffer* lovrBufferCreate(BufferInfo* info, void** data) {
lovrBufferInitFormat(&buffer->format, info);
if (data && *data == NULL) {
gpu_stream* uploads = getUploads();
beginFrame();
gpu_buffer* scratchpad = tempAlloc(gpu_sizeof_buffer());
*data = gpu_map(scratchpad, size, 4, GPU_MAP_WRITE);
gpu_copy_buffers(uploads, scratchpad, buffer->gpu, 0, 0, size);
gpu_copy_buffers(state.stream, scratchpad, buffer->gpu, 0, 0, size);
buffer->sync.writePhase = GPU_PHASE_TRANSFER;
buffer->sync.pendingWrite = GPU_CACHE_TRANSFER_WRITE;
}
return buffer;
@ -778,6 +874,8 @@ Texture* lovrTextureCreate(TextureInfo* info) {
}
}
beginFrame();
gpu_texture_init(texture->gpu, &(gpu_texture_info) {
.type = (gpu_texture_type) info->type,
.format = (gpu_texture_format) info->format,
@ -788,12 +886,13 @@ Texture* lovrTextureCreate(TextureInfo* info) {
((info->usage & TEXTURE_SAMPLE) ? GPU_TEXTURE_SAMPLE : 0) |
((info->usage & TEXTURE_RENDER) ? GPU_TEXTURE_RENDER : 0) |
((info->usage & TEXTURE_STORAGE) ? GPU_TEXTURE_STORAGE : 0) |
((info->usage & TEXTURE_TRANSFER) ? GPU_TEXTURE_COPY_SRC | GPU_TEXTURE_COPY_DST : 0),
((info->usage & TEXTURE_TRANSFER) ? GPU_TEXTURE_COPY_SRC | GPU_TEXTURE_COPY_DST : 0) |
((info->usage == TEXTURE_RENDER) ? GPU_TEXTURE_TRANSIENT : 0),
.srgb = info->srgb,
.handle = info->handle,
.label = info->label,
.upload = {
.stream = getUploads(),
.stream = state.stream,
.buffer = scratchpad,
.levelCount = levelCount,
.levelOffsets = levelOffsets,
@ -819,6 +918,15 @@ Texture* lovrTextureCreate(TextureInfo* info) {
}
}
// Sample-only textures are exempt from sync tracking to reduce overhead. Instead, they are
// manually synchronized with a single barrier after the upload stream.
if (info->usage == TEXTURE_SAMPLE) {
state.hasTextureUpload = true;
} else if (levelCount > 0) {
texture->sync.writePhase = GPU_PHASE_TRANSFER;
texture->sync.pendingWrite = GPU_CACHE_TRANSFER_WRITE;
}
return texture;
}
@ -1397,8 +1505,8 @@ Pass* lovrGraphicsGetPass(PassInfo* info) {
target.color[i].clear[2] = lovrMathGammaToLinear(canvas->clears[i][2]);
target.color[i].clear[3] = canvas->clears[i][2];
gpu_cache cache = GPU_CACHE_BLEND_WRITE | (canvas->loads[i] == LOAD_KEEP ? GPU_CACHE_BLEND_READ : 0);
trackTexture(pass, canvas->textures[i], GPU_PHASE_BLEND, cache);
gpu_cache cache = GPU_CACHE_COLOR_WRITE | (canvas->loads[i] == LOAD_KEEP ? GPU_CACHE_COLOR_READ : 0);
trackTexture(pass, canvas->textures[i], GPU_PHASE_COLOR, cache);
}
if (canvas->depth.texture) {
@ -2418,7 +2526,7 @@ void lovrPassClearBuffer(Pass* pass, Buffer* buffer, uint32_t offset, uint32_t e
lovrCheck(extent % 4 == 0, "Buffer clear extent must be a multiple of 4");
lovrCheck(offset + extent <= buffer->size, "Buffer clear range goes past the end of the Buffer");
gpu_clear_buffer(pass->stream, buffer->gpu, offset, extent);
trackBuffer(pass, buffer, GPU_PHASE_CLEAR, GPU_CACHE_TRANSFER_WRITE);
trackBuffer(pass, buffer, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
}
void lovrPassClearTexture(Pass* pass, Texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount) {
@ -2428,7 +2536,7 @@ void lovrPassClearTexture(Pass* pass, Texture* texture, float value[4], uint32_t
lovrCheck(texture->info.type == TEXTURE_3D || layer + layerCount <= texture->info.depth, "Texture clear range exceeds texture layer count");
lovrCheck(level + levelCount <= texture->info.mipmaps, "Texture clear range exceeds texture mipmap count");
gpu_clear_texture(pass->stream, texture->gpu, value, layer, layerCount, level, levelCount);
trackTexture(pass, texture, GPU_PHASE_CLEAR, GPU_CACHE_TRANSFER_WRITE);
trackTexture(pass, texture, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
}
void lovrPassCopyDataToBuffer(Pass* pass, void* data, Buffer* buffer, uint32_t offset, uint32_t extent) {
@ -2438,7 +2546,7 @@ void lovrPassCopyDataToBuffer(Pass* pass, void* data, Buffer* buffer, uint32_t o
gpu_buffer* scratchpad = tempAlloc(gpu_sizeof_buffer());
void* pointer = gpu_map(scratchpad, extent, 4, GPU_MAP_WRITE);
gpu_copy_buffers(pass->stream, scratchpad, buffer->gpu, 0, offset, extent);
trackBuffer(pass, buffer, GPU_PHASE_COPY, GPU_CACHE_TRANSFER_WRITE);
trackBuffer(pass, buffer, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
memcpy(pointer, data, extent);
}
@ -2448,8 +2556,8 @@ void lovrPassCopyBufferToBuffer(Pass* pass, Buffer* src, Buffer* dst, uint32_t s
lovrCheck(srcOffset + extent <= src->size, "Buffer copy range goes past the end of the source Buffer");
lovrCheck(dstOffset + extent <= dst->size, "Buffer copy range goes past the end of the destination Buffer");
gpu_copy_buffers(pass->stream, src->gpu, dst->gpu, srcOffset, dstOffset, extent);
trackBuffer(pass, src, GPU_PHASE_COPY, GPU_CACHE_TRANSFER_READ);
trackBuffer(pass, dst, GPU_PHASE_COPY, GPU_CACHE_TRANSFER_WRITE);
trackBuffer(pass, src, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_READ);
trackBuffer(pass, dst, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
}
void lovrPassCopyImageToTexture(Pass* pass, Image* image, Texture* texture, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[4]) {
@ -2482,7 +2590,7 @@ void lovrPassCopyImageToTexture(Pass* pass, Image* image, Texture* texture, uint
}
}
gpu_copy_buffer_texture(pass->stream, buffer, texture->gpu, 0, dstOffset, extent);
trackTexture(pass, texture, GPU_PHASE_COPY, GPU_CACHE_TRANSFER_WRITE);
trackTexture(pass, texture, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
}
void lovrPassCopyTextureToTexture(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]) {
@ -2498,8 +2606,8 @@ void lovrPassCopyTextureToTexture(Pass* pass, Texture* src, Texture* dst, uint32
checkTextureBounds(&src->info, srcOffset, extent);
checkTextureBounds(&dst->info, dstOffset, extent);
gpu_copy_textures(pass->stream, src->gpu, dst->gpu, srcOffset, dstOffset, extent);
trackTexture(pass, src, GPU_PHASE_COPY, GPU_CACHE_TRANSFER_READ);
trackTexture(pass, dst, GPU_PHASE_COPY, GPU_CACHE_TRANSFER_WRITE);
trackTexture(pass, src, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_READ);
trackTexture(pass, dst, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
}
void lovrPassBlit(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], FilterMode filter) {
@ -2521,8 +2629,8 @@ void lovrPassBlit(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4],
checkTextureBounds(&src->info, srcOffset, srcExtent);
checkTextureBounds(&dst->info, dstOffset, dstExtent);
gpu_blit(pass->stream, src->gpu, dst->gpu, srcOffset, dstOffset, srcExtent, dstExtent, (gpu_filter) filter);
trackTexture(pass, src, GPU_PHASE_BLIT, GPU_CACHE_TRANSFER_READ);
trackTexture(pass, dst, GPU_PHASE_BLIT, GPU_CACHE_TRANSFER_WRITE);
trackTexture(pass, src, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_READ);
trackTexture(pass, dst, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_WRITE);
}
void lovrPassMipmap(Pass* pass, Texture* texture, uint32_t base, uint32_t count) {
@ -2551,13 +2659,13 @@ void lovrPassMipmap(Pass* pass, Texture* texture, uint32_t base, uint32_t count)
};
gpu_blit(pass->stream, texture->gpu, texture->gpu, srcOffset, dstOffset, srcExtent, dstExtent, GPU_FILTER_LINEAR);
gpu_sync(pass->stream, &(gpu_barrier) {
.prev = GPU_PHASE_BLIT,
.next = GPU_PHASE_BLIT,
.prev = GPU_PHASE_TRANSFER,
.next = GPU_PHASE_TRANSFER,
.flush = GPU_CACHE_TRANSFER_WRITE,
.invalidate = GPU_CACHE_TRANSFER_READ
.clear = GPU_CACHE_TRANSFER_READ
}, 1);
}
trackTexture(pass, texture, GPU_PHASE_BLIT, GPU_CACHE_TRANSFER_READ | GPU_CACHE_TRANSFER_WRITE);
trackTexture(pass, texture, GPU_PHASE_TRANSFER, GPU_CACHE_TRANSFER_READ | GPU_CACHE_TRANSFER_WRITE);
}
// Helpers
@ -2588,17 +2696,7 @@ static void beginFrame(void) {
state.active = true;
state.tick = gpu_begin();
}
static gpu_stream* getUploads(void) {
if (!state.uploads) {
state.uploads = lovrGraphicsGetPass(&(PassInfo) {
.type = PASS_TRANSFER,
.label = "Internal Uploads"
});
}
return state.uploads->stream;
state.stream = gpu_stream_begin("Internal uploads");
}
static uint32_t getLayout(gpu_slot* slots, uint32_t count) {
@ -2823,6 +2921,14 @@ static void trackBuffer(Pass* pass, Buffer* buffer, gpu_phase phase, gpu_cache c
}
static void trackTexture(Pass* pass, Texture* texture, gpu_phase phase, gpu_cache cache) {
if (texture == state.window) {
return;
}
if (texture->info.parent) {
texture = texture->info.parent;
}
if (texture->info.usage == TEXTURE_SAMPLE) {
return; // If the texture is sample-only, no sync needed (initial upload is handled manually)
}