|
|
|
@ -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)
|
|
|
|
|
}
|
|
|
|
|