Temporary Passes;

Sigh, back to getPass.  I don't even know at this point.  Basically now
that we came up with a half-solution for temp buffers, it makes sense to
apply this to passes as well, since we aren't going with the workstream
idea and temp passes are more convenient than retained passes.
This commit is contained in:
bjorn 2022-08-25 21:57:15 -07:00
parent 18413114ad
commit ede1036694
10 changed files with 674 additions and 687 deletions

View File

@ -92,6 +92,10 @@ function lovr.boot()
if lovr.headset and lovr.graphics and conf.window then
lovr.headset.start()
if lovr.headset.getDriver() == 'desktop' then
lovr.mirror = nil
end
end
lovr.handlers = setmetatable({}, { __index = lovr })
@ -128,11 +132,12 @@ function lovr.run()
if not skip then lovr.graphics.submit(pass) end
end
end
if lovr.mirror and lovr.system.isWindowOpen() then
local pass = lovr.graphics.getWindowPass()
pass:reset()
local skip = lovr.mirror(pass)
if not skip then lovr.graphics.submit(pass) end
if lovr.system.isWindowOpen() then
if lovr.mirror then
local pass = lovr.graphics.getWindowPass()
local skip = lovr.mirror(pass)
if not skip then lovr.graphics.submit(pass) end
end
lovr.graphics.present()
end
end
@ -207,7 +212,6 @@ function lovr.errhand(message)
if lovr.system.isWindowOpen() then
local pass = lovr.graphics.getWindowPass()
pass:reset()
render(pass)
lovr.graphics.submit(pass)
lovr.graphics.present()
@ -243,7 +247,8 @@ return function()
while true do
local ok, result, cookie = xpcall(thread, onerror)
if result and ok then -- If step function returned something, exit coroutine and return to C
-- If step function returned something, exit coroutine and return to C
if result and ok then
return result, cookie
elseif not ok then -- Switch to errhand loop
thread = result

View File

@ -28,7 +28,7 @@ struct Draw {
vec4 color;
};
layout(set = 0, binding = 0) uniform Globals { vec4 Resolution; float Time; };
layout(set = 0, binding = 0) uniform Globals { vec2 Resolution; float Time; };
layout(set = 0, binding = 1) uniform CameraBuffer { Camera Cameras[6]; };
layout(set = 0, binding = 2) uniform DrawBuffer { Draw Draws[256]; };
layout(set = 0, binding = 3) uniform sampler Sampler;

View File

@ -463,11 +463,13 @@ static Canvas luax_checkcanvas(lua_State* L, int index) {
};
if (lua_type(L, index) == LUA_TSTRING && !strcmp(lua_tostring(L, index), "window")) {
canvas.count = 1;
canvas.textures[0] = lovrGraphicsGetWindowTexture();
canvas.count = 1;
lovrGraphicsGetBackgroundColor(canvas.clears[0]);
} else if (lua_isuserdata(L, index)) {
canvas.textures[0] = luax_checktype(L, index, Texture);
canvas.count = 1;
canvas.textures[0] = luax_checktype(L, index, Texture);
lovrGraphicsGetBackgroundColor(canvas.clears[0]);
} else if (!lua_istable(L, index)) {
luax_typeerror(L, index, "Texture or table");
} else {
@ -520,12 +522,12 @@ static Canvas luax_checkcanvas(lua_State* L, int index) {
canvas.clears[0][2] = luax_checkfloat(L, -2);
canvas.clears[0][3] = luax_optfloat(L, -1, 1.f);
lua_pop(L, 4);
for (uint32_t i = 1; i < 4; i++) {
for (uint32_t i = 1; i < canvas.count; i++) {
memcpy(canvas.clears[i], canvas.clears[0], 4 * sizeof(float));
}
} else {
lua_pop(L, 1);
for (uint32_t i = 0; i < 4; i++) {
for (uint32_t i = 0; i < canvas.count; i++) {
lua_rawgeti(L, -1, i + 1);
if (lua_istable(L, -1)) {
lua_rawgeti(L, -1, 1);
@ -548,6 +550,10 @@ static Canvas luax_checkcanvas(lua_State* L, int index) {
} else if (!lua_isnil(L, -1)) {
LoadAction load = lua_toboolean(L, -1) ? LOAD_DISCARD : LOAD_KEEP;
canvas.loads[0] = canvas.loads[1] = canvas.loads[2] = canvas.loads[3] = load;
} else {
for (uint32_t i = 0; i < canvas.count; i++) {
lovrGraphicsGetBackgroundColor(canvas.clears[i]);
}
}
lua_pop(L, 1);
@ -1470,7 +1476,7 @@ static int l_lovrGraphicsNewTally(lua_State* L) {
return 1;
}
static int l_lovrGraphicsNewPass(lua_State* L) {
static int l_lovrGraphicsGetPass(lua_State* L) {
PassInfo info = { 0 };
info.type = luax_checkenum(L, 1, PassType, NULL);
@ -1487,7 +1493,7 @@ static int l_lovrGraphicsNewPass(lua_State* L) {
info.label = NULL;
}
Pass* pass = lovrPassCreate(&info);
Pass* pass = lovrGraphicsGetPass(&info);
luax_pushtype(L, Pass, pass);
lovrRelease(pass, lovrPassDestroy);
return 1;
@ -1516,7 +1522,7 @@ static const luaL_Reg lovrGraphics[] = {
{ "newFont", l_lovrGraphicsNewFont },
{ "newModel", l_lovrGraphicsNewModel },
{ "newTally", l_lovrGraphicsNewTally },
{ "newPass", l_lovrGraphicsNewPass },
{ "getPass", l_lovrGraphicsGetPass },
{ NULL, NULL }
};

View File

@ -55,13 +55,13 @@ static int l_lovrPassGetSampleCount(lua_State* L) {
static int l_lovrPassGetTarget(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
int count = (int) lovrPassGetInfo(pass)->canvas.count;
uint32_t count = 0;
Texture *color[4], *depth;
lovrPassGetTarget(pass, color, &depth);
lovrPassGetTarget(pass, color, &depth, &count);
lua_createtable(L, count, !!depth);
for (int i = 0; i < count; i++) {
lua_createtable(L, (int) count, !!depth);
for (int i = 0; i < (int) count; i++) {
luax_pushtype(L, Texture, color[i]);
lua_rawseti(L, -2, i + 1);
}
@ -74,47 +74,18 @@ static int l_lovrPassGetTarget(lua_State* L) {
return 1;
}
static int l_lovrPassSetTarget(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
int count = (int) lovrPassGetInfo(pass)->canvas.count;
Texture* color[4];
Texture* depth = NULL;
if (lua_istable(L, 2)) {
for (int i = 0; i < count; i++) {
lua_rawgeti(L, 2, i + 1);
color[i] = luax_totype(L, -1, Texture);
lovrAssert(color[i], "Expected a Texture for color target #%d", i + 1);
lua_pop(L, 1);
}
lua_getfield(L, 2, "depth");
depth = luax_totype(L, -1, Texture);
lua_pop(L, 1);
} else {
for (int i = 0; i < count; i++) {
color[i] = luax_totype(L, -1, Texture);
lovrAssert(color[i], "Expected a Texture for color target #%d", i + 1);
}
}
lovrPassSetTarget(pass, color, depth);
return 0;
}
static int l_lovrPassGetClear(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
int count = (int) lovrPassGetInfo(pass)->canvas.count;
uint32_t count = 0;
float color[4][4];
float depth;
uint8_t stencil;
lovrPassGetClear(pass, color, &depth, &stencil);
lovrPassGetClear(pass, color, &depth, &stencil, &count);
lua_createtable(L, count, 2);
lua_createtable(L, (int) count, 2);
for (int i = 0; i < count; i++) {
for (int i = 0; i < (int) count; i++) {
lua_createtable(L, 4, 0);
for (int j = 0; j < 4; j++) {
lua_pushnumber(L, color[i][j]);
@ -132,60 +103,6 @@ static int l_lovrPassGetClear(lua_State* L) {
return 1;
}
static int l_lovrPassSetClear(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
int count = (int) lovrPassGetInfo(pass)->canvas.count;
float color[4][4];
float depth;
uint8_t stencil;
lovrPassGetClear(pass, color, &depth, &stencil);
bool table = lua_istable(L, 2);
if (count == 1) {
if (table && luax_len(L, 2) == 1) {
lua_rawgeti(L, 2, 1);
luax_optcolor(L, -1, color[0]);
lua_pop(L, 1);
} else {
luax_readcolor(L, 2, color[0]);
}
} else if (lua_gettop(L) > 2) {
for (int i = 0; i < count; i++) {
if (!lua_isnoneornil(L, 2 + i)) {
luax_optcolor(L, 2 + i, color[i]);
}
}
} else {
for (int i = 0; i < count; i++) {
lua_rawgeti(L, 2, i + 1);
if (!lua_isnil(L, -1)) luax_optcolor(L, -1, color[i]);
lua_pop(L, 1);
}
}
if (table) {
lua_getfield(L, 2, "depth");
depth = luax_optfloat(L, -1, depth);
lua_pop(L, 1);
lua_getfield(L, 2, "stencil");
stencil = luaL_optinteger(L, -1, stencil);
stencil = CLAMP(stencil, 0, 255);
lua_pop(L, 1);
}
lovrPassSetClear(pass, color, depth, stencil);
return 0;
}
static int l_lovrPassReset(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
lovrPassReset(pass);
return 0;
}
static int l_lovrPassGetViewPose(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
uint32_t view = luaL_checkinteger(L, 2) - 1;
@ -1113,13 +1030,8 @@ const luaL_Reg lovrPass[] = {
{ "getDimensions", l_lovrPassGetDimensions },
{ "getViewCount", l_lovrPassGetViewCount },
{ "getSampleCount", l_lovrPassGetSampleCount },
{ "getTarget", l_lovrPassGetTarget },
{ "setTarget", l_lovrPassSetTarget },
{ "getClear", l_lovrPassGetClear },
{ "setClear", l_lovrPassSetClear },
{ "reset", l_lovrPassReset },
{ "getViewPose", l_lovrPassGetViewPose },
{ "setViewPose", l_lovrPassSetViewPose },

View File

@ -9,7 +9,6 @@ typedef struct gpu_layout gpu_layout;
typedef struct gpu_shader gpu_shader;
typedef struct gpu_bundle_pool gpu_bundle_pool;
typedef struct gpu_bundle gpu_bundle;
typedef struct gpu_pass gpu_pass;
typedef struct gpu_pipeline gpu_pipeline;
typedef struct gpu_tally gpu_tally;
typedef struct gpu_stream gpu_stream;
@ -21,7 +20,6 @@ size_t gpu_sizeof_layout(void);
size_t gpu_sizeof_shader(void);
size_t gpu_sizeof_bundle_pool(void);
size_t gpu_sizeof_bundle(void);
size_t gpu_sizeof_pass(void);
size_t gpu_sizeof_pipeline(void);
size_t gpu_sizeof_tally(void);
@ -268,48 +266,6 @@ bool gpu_bundle_pool_init(gpu_bundle_pool* pool, gpu_bundle_pool_info* info);
void gpu_bundle_pool_destroy(gpu_bundle_pool* pool);
void gpu_bundle_write(gpu_bundle** bundles, gpu_bundle_info* info, uint32_t count);
// Pass
typedef enum {
GPU_LOAD_OP_CLEAR,
GPU_LOAD_OP_DISCARD,
GPU_LOAD_OP_KEEP
} gpu_load_op;
typedef enum {
GPU_SAVE_OP_KEEP,
GPU_SAVE_OP_DISCARD
} gpu_save_op;
typedef struct {
gpu_texture_format format;
gpu_load_op load;
gpu_save_op save;
uint8_t usage;
uint8_t resolveUsage;
bool srgb;
} gpu_pass_color_info;
typedef struct {
gpu_texture_format format;
gpu_load_op load;
gpu_save_op save;
uint32_t usage;
} gpu_pass_depth_info;
typedef struct {
gpu_pass_color_info color[4];
gpu_pass_depth_info depth;
uint32_t count;
uint32_t views;
uint32_t samples;
bool resolve;
const char* label;
} gpu_pass_info;
bool gpu_pass_init(gpu_pass* pass, gpu_pass_info* info);
void gpu_pass_destroy(gpu_pass* pass);
// Pipeline
typedef enum {
@ -463,12 +419,13 @@ typedef struct {
} gpu_blend_state;
typedef struct {
gpu_texture_format format;
bool srgb;
gpu_blend_state blend;
uint8_t mask;
} gpu_color_state;
typedef struct {
gpu_pass* pass;
gpu_shader* shader;
gpu_shader_flag* flags;
uint32_t flagCount;
@ -479,7 +436,7 @@ typedef struct {
gpu_depth_state depth;
gpu_stencil_state stencil;
gpu_color_state color[4];
uint32_t colorCount;
uint32_t attachmentCount;
uint32_t viewCount;
const char* label;
} gpu_pipeline_info;
@ -514,23 +471,37 @@ void gpu_tally_destroy(gpu_tally* tally);
// Stream
typedef enum {
GPU_LOAD_OP_CLEAR,
GPU_LOAD_OP_DISCARD,
GPU_LOAD_OP_KEEP
} gpu_load_op;
typedef enum {
GPU_SAVE_OP_KEEP,
GPU_SAVE_OP_DISCARD
} gpu_save_op;
typedef struct {
gpu_texture* texture;
gpu_texture* resolve;
gpu_load_op load;
gpu_save_op save;
float clear[4];
} gpu_color_attachment;
typedef struct {
gpu_texture* texture;
gpu_load_op load, stencilLoad;
gpu_save_op save, stencilSave;
struct { float depth; uint8_t stencil; } clear;
} gpu_depth_attachment;
typedef struct {
gpu_pass* pass;
gpu_color_attachment color[4];
gpu_depth_attachment depth;
uint32_t size[2];
} gpu_render_target;
} gpu_canvas;
typedef enum {
GPU_INDEX_U16,
@ -576,9 +547,9 @@ typedef struct {
gpu_cache clear;
} gpu_barrier;
gpu_stream* gpu_stream_begin();
gpu_stream* gpu_stream_begin(const char* label);
void gpu_stream_end(gpu_stream* stream);
void gpu_render_begin(gpu_stream* stream, gpu_render_target* target);
void gpu_render_begin(gpu_stream* stream, gpu_canvas* canvas);
void gpu_render_end(gpu_stream* stream);
void gpu_compute_begin(gpu_stream* stream);
void gpu_compute_end(gpu_stream* stream);

View File

@ -51,10 +51,6 @@ struct gpu_bundle {
VkDescriptorSet handle;
};
struct gpu_pass {
VkRenderPass handle;
};
struct gpu_pipeline {
VkPipeline handle;
};
@ -74,7 +70,6 @@ size_t gpu_sizeof_layout() { return sizeof(gpu_layout); }
size_t gpu_sizeof_shader() { return sizeof(gpu_shader); }
size_t gpu_sizeof_bundle_pool() { return sizeof(gpu_bundle_pool); }
size_t gpu_sizeof_bundle() { return sizeof(gpu_bundle); }
size_t gpu_sizeof_pass() { return sizeof(gpu_pass); }
size_t gpu_sizeof_pipeline() { return sizeof(gpu_pipeline); }
size_t gpu_sizeof_tally() { return sizeof(gpu_tally); }
@ -122,6 +117,26 @@ typedef struct {
gpu_victim data[256];
} gpu_morgue;
typedef struct {
uint32_t count;
uint32_t views;
uint32_t samples;
bool resolve;
struct {
VkFormat format;
VkImageLayout layout;
VkImageLayout resolveLayout;
gpu_load_op load;
gpu_save_op save;
} color[4];
struct {
VkFormat format;
VkImageLayout layout;
gpu_load_op load;
gpu_save_op save;
} depth;
} gpu_pass_info;
typedef struct {
void* object;
uint64_t hash;
@ -160,6 +175,7 @@ static struct {
gpu_texture surfaceTextures[8];
VkPipelineCache pipelineCache;
VkDebugUtilsMessengerEXT messenger;
gpu_cache_entry renderpasses[16][4];
gpu_cache_entry framebuffers[16][4];
gpu_allocator allocators[GPU_MEMORY_COUNT];
uint8_t allocatorLookup[GPU_MEMORY_COUNT];
@ -191,6 +207,7 @@ static gpu_memory* gpu_allocate(gpu_memory_type type, VkMemoryRequirements info,
static void gpu_release(gpu_memory* memory);
static void condemn(void* handle, VkObjectType type);
static void expunge(void);
static VkRenderPass getCachedRenderPass(gpu_pass_info* pass, bool exact);
static VkFramebuffer getCachedFramebuffer(VkRenderPass pass, VkImageView images[9], uint32_t imageCount, uint32_t size[2]);
static VkImageLayout getNaturalLayout(uint32_t usage, VkImageAspectFlags aspect);
static VkFormat convertFormat(gpu_texture_format format, int colorspace);
@ -1065,108 +1082,6 @@ void gpu_bundle_write(gpu_bundle** bundles, gpu_bundle_info* infos, uint32_t cou
}
}
// Pass
bool gpu_pass_init(gpu_pass* pass, gpu_pass_info* info) {
static const VkAttachmentLoadOp loadOps[] = {
[GPU_LOAD_OP_CLEAR] = VK_ATTACHMENT_LOAD_OP_CLEAR,
[GPU_LOAD_OP_DISCARD] = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
[GPU_LOAD_OP_KEEP] = VK_ATTACHMENT_LOAD_OP_LOAD
};
static const VkAttachmentStoreOp storeOps[] = {
[GPU_SAVE_OP_KEEP] = VK_ATTACHMENT_STORE_OP_STORE,
[GPU_SAVE_OP_DISCARD] = VK_ATTACHMENT_STORE_OP_DONT_CARE
};
VkAttachmentDescription attachments[9];
VkAttachmentReference references[9];
for (uint32_t i = 0; i < info->count; i++) {
references[i].attachment = i;
references[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkImageLayout naturalLayout = getNaturalLayout(info->color[i].usage, VK_IMAGE_ASPECT_COLOR_BIT);
bool surface = info->color[i].format == GPU_FORMAT_SURFACE;
bool discard = surface || info->color[i].load != GPU_LOAD_OP_KEEP;
attachments[i] = (VkAttachmentDescription) {
.format = convertFormat(info->color[i].format, info->color[i].srgb),
.samples = info->samples,
.loadOp = loadOps[info->color[i].load],
.storeOp = info->resolve ? VK_ATTACHMENT_STORE_OP_DONT_CARE : storeOps[info->color[i].save],
.initialLayout = discard ? VK_IMAGE_LAYOUT_UNDEFINED : naturalLayout,
.finalLayout = surface && !info->resolve ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : naturalLayout
};
}
if (info->resolve) {
for (uint32_t i = 0; i < info->count; i++) {
uint32_t index = info->count + i;
references[index].attachment = index;
references[index].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkImageLayout naturalLayout = getNaturalLayout(info->color[i].resolveUsage, VK_IMAGE_ASPECT_COLOR_BIT);
bool surface = info->color[i].format == GPU_FORMAT_SURFACE;
attachments[index] = (VkAttachmentDescription) {
.format = attachments[i].format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.storeOp = storeOps[info->color[i].save],
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = surface ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : naturalLayout
};
}
}
if (info->depth.format) {
uint32_t index = info->count << info->resolve;
references[index].attachment = index;
references[index].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkImageLayout naturalLayout = getNaturalLayout(info->depth.usage, VK_IMAGE_ASPECT_DEPTH_BIT);
attachments[index] = (VkAttachmentDescription) {
.format = convertFormat(info->depth.format, LINEAR),
.samples = info->samples,
.loadOp = loadOps[info->depth.load],
.storeOp = storeOps[info->depth.save],
.stencilLoadOp = loadOps[info->depth.load],
.stencilStoreOp = storeOps[info->depth.save],
.initialLayout = info->depth.load == GPU_LOAD_OP_KEEP ? naturalLayout : VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = naturalLayout
};
}
VkSubpassDescription subpass = {
.colorAttachmentCount = info->count,
.pColorAttachments = &references[0],
.pResolveAttachments = info->resolve ? &references[info->count] : NULL,
.pDepthStencilAttachment = info->depth.format ? &references[info->count << info->resolve] : NULL
};
VkRenderPassMultiviewCreateInfo multiview = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO,
.subpassCount = 1,
.pViewMasks = (uint32_t[1]) { (1 << info->views) - 1 }
};
VkRenderPassCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = info->views > 0 ? &multiview : NULL,
.attachmentCount = (info->count << info->resolve) + !!info->depth.format,
.pAttachments = attachments,
.subpassCount = 1,
.pSubpasses = &subpass
};
VK(vkCreateRenderPass(state.device, &createInfo, NULL, &pass->handle), "Could not create render pass") {
return false;
}
nickname(pass->handle, VK_OBJECT_TYPE_RENDER_PASS, info->label);
return true;
}
void gpu_pass_destroy(gpu_pass* pass) {
condemn(pass->handle, VK_OBJECT_TYPE_RENDER_PASS);
}
// Pipeline
bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info) {
@ -1339,7 +1254,7 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
};
VkPipelineColorBlendAttachmentState colorAttachments[4];
for (uint32_t i = 0; i < info->colorCount; i++) {
for (uint32_t i = 0; i < info->attachmentCount; i++) {
colorAttachments[i] = (VkPipelineColorBlendAttachmentState) {
.blendEnable = info->color[i].blend.enabled,
.srcColorBlendFactor = blendFactors[info->color[i].blend.color.src],
@ -1354,7 +1269,7 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
VkPipelineColorBlendStateCreateInfo colorBlend = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = info->colorCount,
.attachmentCount = info->attachmentCount,
.pAttachments = colorAttachments
};
@ -1415,6 +1330,28 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
}
};
bool resolve = info->multisample.count > 1;
bool depth = info->depth.format;
gpu_pass_info pass = {
.count = (info->attachmentCount << resolve) + depth,
.views = info->viewCount,
.samples = info->multisample.count,
.resolve = resolve,
.depth.format = convertFormat(info->depth.format, LINEAR),
.depth.layout = info->depth.format ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED,
.depth.load = GPU_LOAD_OP_CLEAR,
.depth.save = GPU_SAVE_OP_DISCARD
};
for (uint32_t i = 0; i < info->attachmentCount; i++) {
pass.color[i].format = convertFormat(info->color[i].format, info->color[i].srgb);
pass.color[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
pass.color[i].resolveLayout = resolve ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
pass.color[i].load = GPU_LOAD_OP_CLEAR;
pass.color[i].save = GPU_SAVE_OP_KEEP;
}
VkGraphicsPipelineCreateInfo pipelineInfo = (VkGraphicsPipelineCreateInfo) {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
@ -1428,7 +1365,7 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info)
.pColorBlendState = &colorBlend,
.pDynamicState = &dynamicState,
.layout = info->shader->pipelineLayout,
.renderPass = info->pass->handle
.renderPass = getCachedRenderPass(&pass, false)
};
VK(vkCreateGraphicsPipelines(state.device, state.pipelineCache, 1, &pipelineInfo, NULL, &pipeline->handle), "Could not create pipeline") {
@ -1534,10 +1471,11 @@ void gpu_tally_destroy(gpu_tally* tally) {
// Stream
gpu_stream* gpu_stream_begin() {
gpu_stream* gpu_stream_begin(const char* label) {
gpu_tick* tick = &state.ticks[state.tick[CPU] & TICK_MASK];
CHECK(state.streamCount < COUNTOF(tick->streams), "Too many passes") return NULL;
gpu_stream* stream = &tick->streams[state.streamCount];
nickname(stream->commands, VK_OBJECT_TYPE_COMMAND_BUFFER, label);
VkCommandBufferBeginInfo beginfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
@ -1553,41 +1491,56 @@ void gpu_stream_end(gpu_stream* stream) {
VK(vkEndCommandBuffer(stream->commands), "Failed to end stream") return;
}
void gpu_render_begin(gpu_stream* stream, gpu_render_target* target) {
void gpu_render_begin(gpu_stream* stream, gpu_canvas* canvas) {
gpu_texture* texture = canvas->color[0].texture ? canvas->color[0].texture : canvas->depth.texture;
gpu_pass_info pass = {
.views = texture->layers,
.samples = texture->samples,
.resolve = !!canvas->color[0].resolve
};
VkImageView images[9];
VkClearValue clears[9];
uint32_t count = 0;
for (uint32_t i = 0; i < COUNTOF(target->color) && target->color[i].texture; i++) {
images[i] = target->color[i].texture->view;
memcpy(clears[i].color.float32, target->color[i].clear, 4 * sizeof(float));
count++;
for (uint32_t i = 0; i < COUNTOF(canvas->color) && canvas->color[i].texture; i++) {
images[i] = canvas->color[i].texture->view;
memcpy(clears[i].color.float32, canvas->color[i].clear, 4 * sizeof(float));
pass.color[i].format = convertFormat(canvas->color[i].texture->format, canvas->color[i].texture->srgb);
pass.color[i].layout = canvas->color[i].texture->layout;
pass.color[i].load = canvas->color[i].load;
pass.color[i].save = canvas->color[i].save;
pass.count++;
}
bool resolve = target->color[0].texture && target->color[0].resolve;
if (resolve) {
for (uint32_t i = 0; i < count; i++) {
images[count + i] = target->color[i].resolve->view;
if (pass.resolve) {
for (uint32_t i = 0; i < pass.count; i++) {
images[pass.count + i] = canvas->color[i].resolve->view;
pass.color[i].resolveLayout = canvas->color[i].resolve->layout;
}
count <<= 1;
pass.count <<= 1;
}
if (target->depth.texture) {
uint32_t index = count++;
images[index] = target->depth.texture->view;
clears[index].depthStencil.depth = target->depth.clear.depth;
clears[index].depthStencil.stencil = target->depth.clear.stencil;
if (canvas->depth.texture) {
uint32_t index = pass.count++;
images[index] = canvas->depth.texture->view;
clears[index].depthStencil.depth = canvas->depth.clear.depth;
clears[index].depthStencil.stencil = canvas->depth.clear.stencil;
pass.depth.format = convertFormat(canvas->depth.texture->format, LINEAR);
pass.depth.layout = canvas->depth.texture->layout;
pass.depth.load = canvas->depth.load;
pass.depth.save = canvas->depth.save;
}
VkFramebuffer framebuffer = getCachedFramebuffer(target->pass->handle, images, count, target->size);
VkRenderPass renderPass = getCachedRenderPass(&pass, true);
VkFramebuffer framebuffer = getCachedFramebuffer(renderPass, images, pass.count, canvas->size);
VkRenderPassBeginInfo beginfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = target->pass->handle,
.renderPass = renderPass,
.framebuffer = framebuffer,
.renderArea = { { 0, 0 }, { target->size[0], target->size[1] } },
.clearValueCount = count,
.renderArea = { { 0, 0 }, { canvas->size[0], canvas->size[1] } },
.clearValueCount = pass.count,
.pClearValues = clears
};
@ -2354,6 +2307,12 @@ void gpu_destroy(void) {
if (framebuffer) vkDestroyFramebuffer(state.device, framebuffer, NULL);
}
}
for (uint32_t i = 0; i < COUNTOF(state.renderpasses); i++) {
for (uint32_t j = 0; j < COUNTOF(state.renderpasses[0]); j++) {
VkRenderPass pass = state.renderpasses[i][j].object;
if (pass) vkDestroyRenderPass(state.device, pass, NULL);
}
}
for (uint32_t i = 0; i < COUNTOF(state.memory); i++) {
if (state.memory[i].handle) vkFreeMemory(state.device, state.memory[i].handle, NULL);
}
@ -2585,6 +2544,170 @@ static void expunge() {
}
}
// Ugliness until we can use dynamic rendering
static VkRenderPass getCachedRenderPass(gpu_pass_info* pass, bool exact) {
bool depth = pass->depth.layout != VK_IMAGE_LAYOUT_UNDEFINED;
uint32_t count = (pass->count - depth) >> pass->resolve;
uint32_t lower[] = {
count > 0 ? pass->color[0].format : 0xff,
count > 1 ? pass->color[1].format : 0xff,
count > 2 ? pass->color[2].format : 0xff,
count > 3 ? pass->color[3].format : 0xff,
depth ? pass->depth.format : 0xff,
pass->samples,
pass->resolve,
pass->views
};
uint32_t upper[] = {
count > 0 ? pass->color[0].load : 0xff,
count > 1 ? pass->color[1].load : 0xff,
count > 2 ? pass->color[2].load : 0xff,
count > 3 ? pass->color[3].load : 0xff,
count > 0 ? pass->color[0].save : 0xff,
count > 1 ? pass->color[1].save : 0xff,
count > 2 ? pass->color[2].save : 0xff,
count > 3 ? pass->color[3].save : 0xff,
depth ? pass->depth.load : 0xff,
depth ? pass->depth.save : 0xff,
count > 0 ? pass->color[0].layout : 0x00,
count > 1 ? pass->color[1].layout : 0x00,
count > 2 ? pass->color[2].layout : 0x00,
count > 3 ? pass->color[3].layout : 0x00,
pass->resolve && count > 0 ? pass->color[0].resolveLayout : 0x00,
pass->resolve && count > 1 ? pass->color[1].resolveLayout : 0x00,
pass->resolve && count > 2 ? pass->color[2].resolveLayout : 0x00,
pass->resolve && count > 3 ? pass->color[3].resolveLayout : 0x00,
depth ? pass->depth.layout : 0x00,
0
};
// The lower half of the hash contains format, sample, multiview info, which is all that's needed
// to select a "compatible" render pass (which is all that's needed for creating pipelines).
// The upper half of the hash contains load/store info and usage flags (for layout transitions),
// which is necessary to select an "exact" match when e.g. actually beginning a render pass
uint64_t hash = ((uint64_t) hash32(HASH_SEED, upper, sizeof(upper)) << 32) | hash32(HASH_SEED, lower, sizeof(lower));
uint64_t mask = exact ? ~0ull : ~0u;
// Search for a pass, they are always stored in MRU order, which requires moving it to the first
// column if you end up using it, and shifting down the rest (dunno if that's actually worth it)
uint32_t rows = COUNTOF(state.renderpasses);
uint32_t cols = COUNTOF(state.renderpasses[0]);
gpu_cache_entry* row = state.renderpasses[hash & (rows - 1)];
for (uint32_t i = 0; i < cols && row[i].object; i++) {
if ((row[i].hash & mask) == hash) {
gpu_cache_entry entry = row[i];
if (i > 0) {
for (uint32_t j = i; j >= 1; j--) {
row[j] = row[j - 1];
}
row[0] = entry;
}
return entry.object;
}
}
// If no render pass was found, make a new one, potentially condemning and evicting an old one
static const VkAttachmentLoadOp loadOps[] = {
[GPU_LOAD_OP_CLEAR] = VK_ATTACHMENT_LOAD_OP_CLEAR,
[GPU_LOAD_OP_DISCARD] = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
[GPU_LOAD_OP_KEEP] = VK_ATTACHMENT_LOAD_OP_LOAD
};
static const VkAttachmentStoreOp storeOps[] = {
[GPU_SAVE_OP_KEEP] = VK_ATTACHMENT_STORE_OP_STORE,
[GPU_SAVE_OP_DISCARD] = VK_ATTACHMENT_STORE_OP_DONT_CARE
};
VkAttachmentDescription attachments[9];
VkAttachmentReference references[9];
for (uint32_t i = 0; i < count; i++) {
references[i].attachment = i;
references[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
bool surface = pass->color[i].format == state.surfaceFormat;
bool discard = surface || pass->color[i].load != GPU_LOAD_OP_KEEP;
attachments[i] = (VkAttachmentDescription) {
.format = pass->color[i].format,
.samples = pass->samples,
.loadOp = loadOps[pass->color[i].load],
.storeOp = pass->resolve ? VK_ATTACHMENT_STORE_OP_DONT_CARE : storeOps[pass->color[i].save],
.initialLayout = discard ? VK_IMAGE_LAYOUT_UNDEFINED : pass->color[i].layout,
.finalLayout = surface && !pass->resolve ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : pass->color[i].layout
};
}
if (pass->resolve) {
for (uint32_t i = 0; i < count; i++) {
uint32_t index = count + i;
references[index].attachment = index;
references[index].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
bool surface = pass->color[i].format == state.surfaceFormat;
attachments[index] = (VkAttachmentDescription) {
.format = pass->color[i].format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.storeOp = storeOps[pass->color[i].save],
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = surface ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : pass->color[i].resolveLayout
};
}
}
if (depth) {
uint32_t index = pass->count - 1;
references[index].attachment = index;
references[index].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[index] = (VkAttachmentDescription) {
.format = pass->depth.format,
.samples = pass->samples,
.loadOp = loadOps[pass->depth.load],
.storeOp = storeOps[pass->depth.save],
.stencilLoadOp = loadOps[pass->depth.load],
.stencilStoreOp = storeOps[pass->depth.save],
.initialLayout = pass->depth.load == GPU_LOAD_OP_KEEP ? pass->depth.layout : VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = pass->depth.layout
};
}
VkSubpassDescription subpass = {
.colorAttachmentCount = count,
.pColorAttachments = &references[0],
.pResolveAttachments = pass->resolve ? &references[count] : NULL,
.pDepthStencilAttachment = depth ? &references[pass->count - 1] : NULL
};
VkRenderPassMultiviewCreateInfo multiview = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO,
.subpassCount = 1,
.pViewMasks = (uint32_t[1]) { (1 << pass->views) - 1 }
};
VkRenderPassCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = pass->views > 0 ? &multiview : NULL,
.attachmentCount = pass->count,
.pAttachments = attachments,
.subpassCount = 1,
.pSubpasses = &subpass
};
VkRenderPass handle;
VK(vkCreateRenderPass(state.device, &info, NULL, &handle), "Could not create render pass") {
return VK_NULL_HANDLE;
}
condemn(row[cols - 1].object, VK_OBJECT_TYPE_RENDER_PASS);
memmove(row + 1, row, (cols - 1) * sizeof(row[0]));
row[0].object = handle;
row[0].hash = hash;
return handle;
}
VkFramebuffer getCachedFramebuffer(VkRenderPass pass, VkImageView images[9], uint32_t imageCount, uint32_t size[2]) {
uint32_t hash = HASH_SEED;
hash = hash32(hash, images, imageCount * sizeof(images[0]));

File diff suppressed because it is too large Load Diff

View File

@ -577,20 +577,15 @@ typedef struct {
} PassInfo;
Pass* lovrGraphicsGetWindowPass(void);
Pass* lovrPassCreate(PassInfo* info);
Pass* lovrGraphicsGetPass(PassInfo* info);
void lovrPassDestroy(void* ref);
const PassInfo* lovrPassGetInfo(Pass* pass);
uint32_t lovrPassGetWidth(Pass* pass);
uint32_t lovrPassGetHeight(Pass* pass);
uint32_t lovrPassGetViewCount(Pass* pass);
uint32_t lovrPassGetSampleCount(Pass* pass);
void lovrPassGetTarget(Pass* pass, Texture* color[4], Texture** depth);
void lovrPassSetTarget(Pass* pass, Texture* color[4], Texture* depth);
void lovrPassGetClear(Pass* pass, float color[4][4], float* depth, uint8_t* stencil);
void lovrPassSetClear(Pass* pass, float color[4][4], float depth, uint8_t stencil);
void lovrPassReset(Pass* pass);
void lovrPassGetTarget(Pass* pass, Texture* color[4], Texture** depth, uint32_t* count);
void lovrPassGetClear(Pass* pass, float color[4][4], float* depth, uint8_t* stencil, uint32_t* count);
void lovrPassGetViewMatrix(Pass* pass, uint32_t index, float viewMatrix[16]);
void lovrPassSetViewMatrix(Pass* pass, uint32_t index, float viewMatrix[16]);

View File

@ -194,7 +194,6 @@ static Texture* desktop_getTexture(void) {
static Pass* desktop_getPass(void) {
Pass* pass = lovrGraphicsGetWindowPass();
lovrPassReset(pass);
float position[4], orientation[4];
desktop_getViewPose(0, position, orientation);

View File

@ -980,19 +980,6 @@ static void openxr_start(void) {
}
}
state.pass = lovrPassCreate(&(PassInfo) {
.type = PASS_RENDER,
.canvas.count = 1,
.canvas.textures[0] = state.textures[COLOR][0],
.canvas.loads[0] = LOAD_CLEAR,
.canvas.clears[0] = { 0.f, 0.f, 0.f, 0.f },
.canvas.depth.texture = state.textures[DEPTH][0],
.canvas.depth.format = state.config.stencil ? FORMAT_D32FS8 : FORMAT_D32F,
.canvas.depth.load = LOAD_CLEAR,
.canvas.depth.clear = 0.f,
.canvas.samples = state.config.antialias ? 4 : 1
});
XrCompositionLayerFlags layerFlags = 0;
if (state.features.overlay) {
@ -1040,8 +1027,6 @@ static void openxr_stop(void) {
return;
}
lovrRelease(state.pass, lovrPassDestroy);
for (uint32_t i = 0; i < 2; i++) {
for (uint32_t j = 0; j < state.textureCount[i]; j++) {
lovrRelease(state.textures[i][j], lovrTextureDestroy);
@ -1818,13 +1803,23 @@ static Pass* openxr_getPass(void) {
return NULL;
}
uint8_t stencil;
float color[4][4], depth;
lovrPassGetClear(state.pass, color, &depth, &stencil);
lovrGraphicsGetBackgroundColor(color[0]);
lovrPassSetClear(state.pass, color, depth, stencil);
lovrPassSetTarget(state.pass, &texture, depthTexture);
lovrPassReset(state.pass);
Canvas canvas = {
.count = 1,
.textures[0] = texture,
.depth.texture = depthTexture,
.depth.format = state.config.stencil ? FORMAT_D32FS8 : FORMAT_D32F,
.depth.load = LOAD_CLEAR,
.depth.clear = 0.f,
.samples = state.config.antialias ? 4 : 1
};
lovrGraphicsGetBackgroundColor(canvas.clears[0]);
state.pass = lovrGraphicsGetPass(&(PassInfo) {
.type = PASS_RENDER,
.label = "Headset",
.canvas = canvas
});
uint32_t count;
XrView views[2];