Converted to render graph, and made hex region list dynamic
parent
9016fda882
commit
bde72b0a15
@ -0,0 +1,104 @@
|
||||
#ifndef RENDER_GRAPH_H
|
||||
#define RENDER_GRAPH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "gpu.h"
|
||||
|
||||
typedef struct UIContextStruct UIContext;
|
||||
typedef struct HexContextStruct HexContext;
|
||||
|
||||
typedef uint32_t RGResource;
|
||||
#define RG_RESOURCE_SWAPCHAIN UINT32_MAX
|
||||
|
||||
typedef struct RGResourceUsageStruct {
|
||||
RGResource resource[MAX_FRAMES_IN_FLIGHT];
|
||||
VkPipelineStageFlags stage;
|
||||
VkAccessFlags access;
|
||||
VkImageLayout layout;
|
||||
} RGResourceUsage;
|
||||
|
||||
typedef enum {
|
||||
RG_NODE_INPUT = 0,
|
||||
RG_NODE_COMPUTE = 1,
|
||||
RG_NODE_RENDER = 2,
|
||||
RG_NODE_DISPLAY = 3,
|
||||
} RGNodeType;
|
||||
|
||||
typedef void (*RGRecordFn)(
|
||||
VkCommandBuffer,
|
||||
void* userdata,
|
||||
RenderContext*,
|
||||
UIContext*,
|
||||
HexContext*,
|
||||
uint32_t image_index,
|
||||
uint32_t frame,
|
||||
double time);
|
||||
|
||||
typedef struct RGBarrierStruct {
|
||||
VkImageMemoryBarrier barrier;
|
||||
VkPipelineStageFlags src_stage;
|
||||
VkPipelineStageFlags dst_stage;
|
||||
RGResource resource[MAX_FRAMES_IN_FLIGHT];
|
||||
} RGBarrier;
|
||||
|
||||
#define RG_MAX_NODES 64
|
||||
#define RG_MAX_RESOURCES_PER_NODE 16
|
||||
|
||||
typedef struct RGNodeStruct {
|
||||
RGNodeType type;
|
||||
RGResourceUsage reads[RG_MAX_RESOURCES_PER_NODE];
|
||||
uint32_t read_count;
|
||||
RGResourceUsage writes[RG_MAX_RESOURCES_PER_NODE];
|
||||
uint32_t write_count;
|
||||
RGBarrier pre_barriers[RG_MAX_RESOURCES_PER_NODE];
|
||||
uint32_t pre_barrier_count;
|
||||
RGRecordFn record;
|
||||
void* userdata;
|
||||
bool active;
|
||||
} RGNode;
|
||||
|
||||
typedef struct RenderGraphStruct {
|
||||
RGNode nodes[RG_MAX_NODES];
|
||||
uint32_t node_count;
|
||||
uint32_t topo_order[RG_MAX_NODES];
|
||||
uint32_t topo_count;
|
||||
bool compiled;
|
||||
} RenderGraph;
|
||||
|
||||
// Returns stable node handle (index). Copies reads/writes arrays internally.
|
||||
uint32_t render_graph_add_node(
|
||||
RenderGraph*,
|
||||
RGNodeType,
|
||||
RGResourceUsage* reads, uint32_t read_count,
|
||||
RGResourceUsage* writes, uint32_t write_count,
|
||||
RGRecordFn, void* userdata);
|
||||
|
||||
// Permanent-node helpers — call once at startup from run_app.
|
||||
// Each registers the appropriate node with the correct callback and resource declarations.
|
||||
uint32_t render_graph_add_ui_compute_node(RenderGraph*);
|
||||
uint32_t render_graph_add_ui_composite_node(RenderGraph*);
|
||||
uint32_t render_graph_add_display_node(RenderGraph*);
|
||||
|
||||
// Convenience: registers a camera's hex-draw RENDER node with correct resource declarations.
|
||||
struct CameraStruct;
|
||||
uint32_t render_graph_add_camera_node(RenderGraph*, struct CameraStruct*);
|
||||
|
||||
void render_graph_remove_node(RenderGraph*, uint32_t handle);
|
||||
|
||||
// Derives topo order and image barriers from declared resource usages.
|
||||
void render_graph_compile(RenderGraph*, UIContext* ui);
|
||||
|
||||
// Records all nodes into their respective command buffers in topo order.
|
||||
// COMPUTE nodes → compute_cmd. RENDER/DISPLAY nodes → graphics_cmd.
|
||||
// Caller handles queue submit and semaphore chain.
|
||||
VkResult render_graph_execute(
|
||||
RenderGraph*,
|
||||
VkCommandBuffer graphics_cmd,
|
||||
VkCommandBuffer compute_cmd,
|
||||
RenderContext*, UIContext*, HexContext*,
|
||||
uint32_t image_index, double time);
|
||||
|
||||
void render_graph_invalidate(RenderGraph*);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,7 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
void main() {
|
||||
color = vec4(1, 1, 1, 1);
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
#version 450
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(0, 0, 0, 1);
|
||||
}
|
||||
@ -0,0 +1,468 @@
|
||||
#include "render_graph.h"
|
||||
#include "draw.h"
|
||||
#include "camera.h"
|
||||
#include "ui.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Record callbacks (shims that drive the actual render/compute functions)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void record_hex_draw_for_camera(
|
||||
VkCommandBuffer cmd,
|
||||
void* userdata,
|
||||
RenderContext* gpu,
|
||||
UIContext* ui,
|
||||
HexContext* hex,
|
||||
uint32_t image_index,
|
||||
uint32_t frame,
|
||||
double time) {
|
||||
(void)gpu; (void)image_index;
|
||||
Camera* cam = (Camera*)userdata;
|
||||
|
||||
VkExtent2D extent = {cam->texture.width, cam->texture.height};
|
||||
VkViewport viewport = {
|
||||
.width = (float)extent.width,
|
||||
.height = (float)extent.height,
|
||||
.maxDepth = 1.0f,
|
||||
.minDepth = 0.0f,
|
||||
};
|
||||
vkCmdSetViewport(cmd, 0, 1, &viewport);
|
||||
VkRect2D scissor = {.extent = extent};
|
||||
vkCmdSetScissor(cmd, 0, 1, &scissor);
|
||||
|
||||
Texture* target = &ui->texture_slots[cam->texture.texture_slot[frame]];
|
||||
|
||||
VkRenderingAttachmentInfo cam_color = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = target->view,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.clearValue = {.color = {{0.0f, 0.0f, 0.0f, 0.0f}}},
|
||||
};
|
||||
VkRenderingAttachmentInfo cam_depth = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = cam->texture.depth_image_view[frame],
|
||||
.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.clearValue = {.depthStencil = {1.0f, 0}},
|
||||
};
|
||||
VkRenderingInfo cam_rendering = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
|
||||
.renderArea = {{0, 0}, extent},
|
||||
.layerCount = 1,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &cam_color,
|
||||
.pDepthAttachment = &cam_depth,
|
||||
};
|
||||
vkCmdBeginRendering(cmd, &cam_rendering);
|
||||
record_hex_draw(cmd, hex, cam->gpu_address[frame], time, frame);
|
||||
vkCmdEndRendering(cmd);
|
||||
}
|
||||
|
||||
static void record_container_draw_wrapper(
|
||||
VkCommandBuffer cmd,
|
||||
void* userdata,
|
||||
RenderContext* gpu,
|
||||
UIContext* ui,
|
||||
HexContext* hex,
|
||||
uint32_t image_index,
|
||||
uint32_t frame,
|
||||
double time) {
|
||||
(void)userdata; (void)hex;
|
||||
|
||||
VkViewport viewport = {
|
||||
.width = (float)gpu->swapchain_extent.width,
|
||||
.height = (float)gpu->swapchain_extent.height,
|
||||
.maxDepth = 1.0f,
|
||||
.minDepth = 0.0f,
|
||||
};
|
||||
vkCmdSetViewport(cmd, 0, 1, &viewport);
|
||||
VkRect2D scissor = {.extent = gpu->swapchain_extent};
|
||||
vkCmdSetScissor(cmd, 0, 1, &scissor);
|
||||
|
||||
VkRenderingAttachmentInfo color_attachment = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = gpu->swapchain_image_views[image_index],
|
||||
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.clearValue = {.color = {{0.0f, 0.0f, 0.0f, 0.0f}}},
|
||||
};
|
||||
VkRenderingAttachmentInfo depth_attachment = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = gpu->depth_image_view,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.clearValue = {.depthStencil = {1.0f, 0}},
|
||||
};
|
||||
VkRenderingInfo scene_rendering = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
|
||||
.renderArea = {{0, 0}, gpu->swapchain_extent},
|
||||
.layerCount = 1,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &color_attachment,
|
||||
.pDepthAttachment = &depth_attachment,
|
||||
};
|
||||
vkCmdBeginRendering(cmd, &scene_rendering);
|
||||
record_container_draw(cmd, gpu, ui, time, frame);
|
||||
vkCmdEndRendering(cmd);
|
||||
}
|
||||
|
||||
static void record_ui_compute_wrapper(
|
||||
VkCommandBuffer cmd,
|
||||
void* userdata,
|
||||
RenderContext* gpu,
|
||||
UIContext* ui,
|
||||
HexContext* hex,
|
||||
uint32_t image_index,
|
||||
uint32_t frame,
|
||||
double time) {
|
||||
(void)userdata; (void)gpu; (void)hex; (void)image_index; (void)time;
|
||||
|
||||
VkMemoryBarrier compute_barrier = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT
|
||||
| VK_ACCESS_TRANSFER_READ_BIT
|
||||
| VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmd,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
0, 1, &compute_barrier, 0, NULL, 0, NULL);
|
||||
|
||||
record_ui_compute(cmd, ui, frame);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
uint32_t render_graph_add_node(
|
||||
RenderGraph* graph,
|
||||
RGNodeType type,
|
||||
RGResourceUsage* reads, uint32_t read_count,
|
||||
RGResourceUsage* writes, uint32_t write_count,
|
||||
RGRecordFn record, void* userdata) {
|
||||
assert(read_count <= RG_MAX_RESOURCES_PER_NODE);
|
||||
assert(write_count <= RG_MAX_RESOURCES_PER_NODE);
|
||||
|
||||
for(uint32_t i = 0; i < RG_MAX_NODES; i++) {
|
||||
if(!graph->nodes[i].active) {
|
||||
RGNode* node = &graph->nodes[i];
|
||||
memset(node, 0, sizeof(RGNode));
|
||||
node->type = type;
|
||||
if(reads && read_count > 0) {
|
||||
memcpy(node->reads, reads, read_count * sizeof(RGResourceUsage));
|
||||
node->read_count = read_count;
|
||||
}
|
||||
if(writes && write_count > 0) {
|
||||
memcpy(node->writes, writes, write_count * sizeof(RGResourceUsage));
|
||||
node->write_count = write_count;
|
||||
}
|
||||
node->record = record;
|
||||
node->userdata = userdata;
|
||||
node->active = true;
|
||||
if(i + 1 > graph->node_count) graph->node_count = i + 1;
|
||||
graph->compiled = false;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
assert(false && "RG_MAX_NODES exceeded");
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
uint32_t render_graph_add_ui_compute_node(RenderGraph* graph) {
|
||||
return render_graph_add_node(graph, RG_NODE_COMPUTE,
|
||||
NULL, 0, NULL, 0,
|
||||
record_ui_compute_wrapper, NULL);
|
||||
}
|
||||
|
||||
uint32_t render_graph_add_ui_composite_node(RenderGraph* graph) {
|
||||
RGResourceUsage ui_writes[] = {{
|
||||
.resource = {RG_RESOURCE_SWAPCHAIN, RG_RESOURCE_SWAPCHAIN},
|
||||
.stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
}};
|
||||
// reads are rebuilt at compile time from camera RENDER nodes' writes
|
||||
return render_graph_add_node(graph, RG_NODE_RENDER,
|
||||
NULL, 0,
|
||||
ui_writes, 1,
|
||||
record_container_draw_wrapper, NULL);
|
||||
}
|
||||
|
||||
uint32_t render_graph_add_display_node(RenderGraph* graph) {
|
||||
// Reading PRESENT_SRC_KHR creates a dependency edge from the UI composite
|
||||
// (which writes the swapchain) → this node, ensuring correct topo order
|
||||
// and auto-deriving the present barrier.
|
||||
RGResourceUsage display_reads[] = {{
|
||||
.resource = {RG_RESOURCE_SWAPCHAIN, RG_RESOURCE_SWAPCHAIN},
|
||||
.stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
.access = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
}};
|
||||
return render_graph_add_node(graph, RG_NODE_DISPLAY,
|
||||
display_reads, 1,
|
||||
NULL, 0,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
uint32_t render_graph_add_camera_node(RenderGraph* graph, struct CameraStruct* camera) {
|
||||
RGResourceUsage cam_writes[] = {{
|
||||
.resource = {camera->texture.texture_slot[0], camera->texture.texture_slot[1]},
|
||||
.stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
}};
|
||||
return render_graph_add_node(graph, RG_NODE_RENDER,
|
||||
NULL, 0,
|
||||
cam_writes, 1,
|
||||
record_hex_draw_for_camera, camera);
|
||||
}
|
||||
|
||||
void render_graph_remove_node(RenderGraph* graph, uint32_t handle) {
|
||||
if(handle >= RG_MAX_NODES) return;
|
||||
graph->nodes[handle].active = false;
|
||||
graph->compiled = false;
|
||||
}
|
||||
|
||||
void render_graph_compile(RenderGraph* graph, UIContext* ui) {
|
||||
(void)ui;
|
||||
|
||||
// Step 1: Rebuild the UI composite node's reads from all other RENDER nodes'
|
||||
// writes, so that topo sort derives the camera → UI composite dependency.
|
||||
// The UI composite is identified as the RENDER node that writes RG_RESOURCE_SWAPCHAIN.
|
||||
uint32_t ui_composite = UINT32_MAX;
|
||||
for(uint32_t i = 0; i < graph->node_count; i++) {
|
||||
if(!graph->nodes[i].active || graph->nodes[i].type != RG_NODE_RENDER) continue;
|
||||
for(uint32_t j = 0; j < graph->nodes[i].write_count; j++) {
|
||||
if(graph->nodes[i].writes[j].resource[0] == RG_RESOURCE_SWAPCHAIN) {
|
||||
ui_composite = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(ui_composite != UINT32_MAX) break;
|
||||
}
|
||||
|
||||
if(ui_composite != UINT32_MAX) {
|
||||
RGNode* ui_node = &graph->nodes[ui_composite];
|
||||
ui_node->read_count = 0;
|
||||
for(uint32_t i = 0; i < graph->node_count; i++) {
|
||||
if(!graph->nodes[i].active || i == ui_composite) continue;
|
||||
if(graph->nodes[i].type != RG_NODE_RENDER) continue;
|
||||
for(uint32_t j = 0; j < graph->nodes[i].write_count; j++) {
|
||||
if(ui_node->read_count >= RG_MAX_RESOURCES_PER_NODE) break;
|
||||
ui_node->reads[ui_node->read_count++] = (RGResourceUsage){
|
||||
.resource = {
|
||||
graph->nodes[i].writes[j].resource[0],
|
||||
graph->nodes[i].writes[j].resource[1],
|
||||
},
|
||||
.stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
.access = VK_ACCESS_SHADER_READ_BIT,
|
||||
.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Topological sort (Kahn's algorithm).
|
||||
graph->topo_count = 0;
|
||||
|
||||
uint32_t active[RG_MAX_NODES];
|
||||
uint32_t active_count = 0;
|
||||
for(uint32_t i = 0; i < graph->node_count; i++) {
|
||||
if(graph->nodes[i].active) active[active_count++] = i;
|
||||
}
|
||||
|
||||
// Edge matrix: edges[w][r] = true means node w must execute before node r.
|
||||
bool edges[RG_MAX_NODES][RG_MAX_NODES];
|
||||
uint32_t in_degree[RG_MAX_NODES];
|
||||
memset(edges, 0, sizeof(edges));
|
||||
memset(in_degree, 0, sizeof(in_degree));
|
||||
|
||||
for(uint32_t ai = 0; ai < active_count; ai++) {
|
||||
uint32_t r = active[ai];
|
||||
for(uint32_t ri = 0; ri < graph->nodes[r].read_count; ri++) {
|
||||
RGResource res0 = graph->nodes[r].reads[ri].resource[0];
|
||||
for(uint32_t bi = 0; bi < active_count; bi++) {
|
||||
uint32_t w = active[bi];
|
||||
if(w == r) continue;
|
||||
for(uint32_t wi = 0; wi < graph->nodes[w].write_count; wi++) {
|
||||
if(graph->nodes[w].writes[wi].resource[0] == res0) {
|
||||
if(!edges[w][r]) {
|
||||
edges[w][r] = true;
|
||||
in_degree[r]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t queue[RG_MAX_NODES];
|
||||
uint32_t qhead = 0, qtail = 0;
|
||||
for(uint32_t ai = 0; ai < active_count; ai++) {
|
||||
if(in_degree[active[ai]] == 0) queue[qtail++] = active[ai];
|
||||
}
|
||||
while(qhead < qtail) {
|
||||
uint32_t n = queue[qhead++];
|
||||
graph->topo_order[graph->topo_count++] = n;
|
||||
for(uint32_t ai = 0; ai < active_count; ai++) {
|
||||
uint32_t m = active[ai];
|
||||
if(edges[n][m] && --in_degree[m] == 0) queue[qtail++] = m;
|
||||
}
|
||||
}
|
||||
assert(graph->topo_count == active_count && "render graph has a cycle");
|
||||
|
||||
// Step 3: Derive image barriers.
|
||||
// For each resource a node WRITES: find the prior writer's layout, or
|
||||
// UNDEFINED if none → emit a pre-write (acquire) barrier.
|
||||
// For each resource a node READS: find the most recent writer's layout
|
||||
// → emit a pre-read (transition) barrier.
|
||||
for(uint32_t ti = 0; ti < graph->topo_count; ti++) {
|
||||
uint32_t n = graph->topo_order[ti];
|
||||
RGNode* node = &graph->nodes[n];
|
||||
node->pre_barrier_count = 0;
|
||||
|
||||
VkImageSubresourceRange color_range = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
// Pre-write barriers
|
||||
for(uint32_t wi = 0; wi < node->write_count; wi++) {
|
||||
RGResource res0 = node->writes[wi].resource[0];
|
||||
VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkAccessFlags src_access = 0;
|
||||
VkImageLayout old_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
for(int32_t tj = (int32_t)ti - 1; tj >= 0; tj--) {
|
||||
uint32_t m = graph->topo_order[tj];
|
||||
bool found = false;
|
||||
for(uint32_t pwi = 0; pwi < graph->nodes[m].write_count; pwi++) {
|
||||
if(graph->nodes[m].writes[pwi].resource[0] == res0) {
|
||||
src_stage = graph->nodes[m].writes[pwi].stage;
|
||||
src_access = graph->nodes[m].writes[pwi].access;
|
||||
old_layout = graph->nodes[m].writes[pwi].layout;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found) break;
|
||||
}
|
||||
|
||||
if(node->pre_barrier_count < RG_MAX_RESOURCES_PER_NODE) {
|
||||
RGBarrier* bar = &node->pre_barriers[node->pre_barrier_count++];
|
||||
bar->resource[0] = node->writes[wi].resource[0];
|
||||
bar->resource[1] = node->writes[wi].resource[1];
|
||||
bar->src_stage = src_stage;
|
||||
bar->dst_stage = node->writes[wi].stage;
|
||||
bar->barrier = (VkImageMemoryBarrier){
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = src_access,
|
||||
.dstAccessMask = node->writes[wi].access,
|
||||
.oldLayout = old_layout,
|
||||
.newLayout = node->writes[wi].layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = VK_NULL_HANDLE,
|
||||
.subresourceRange = color_range,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-read barriers
|
||||
for(uint32_t ri = 0; ri < node->read_count; ri++) {
|
||||
RGResource res0 = node->reads[ri].resource[0];
|
||||
|
||||
for(int32_t tj = (int32_t)ti - 1; tj >= 0; tj--) {
|
||||
uint32_t m = graph->topo_order[tj];
|
||||
bool found = false;
|
||||
for(uint32_t pwi = 0; pwi < graph->nodes[m].write_count; pwi++) {
|
||||
if(graph->nodes[m].writes[pwi].resource[0] == res0) {
|
||||
if(node->pre_barrier_count < RG_MAX_RESOURCES_PER_NODE) {
|
||||
RGBarrier* bar = &node->pre_barriers[node->pre_barrier_count++];
|
||||
bar->resource[0] = node->reads[ri].resource[0];
|
||||
bar->resource[1] = node->reads[ri].resource[1];
|
||||
bar->src_stage = graph->nodes[m].writes[pwi].stage;
|
||||
bar->dst_stage = node->reads[ri].stage;
|
||||
bar->barrier = (VkImageMemoryBarrier){
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = graph->nodes[m].writes[pwi].access,
|
||||
.dstAccessMask = node->reads[ri].access,
|
||||
.oldLayout = graph->nodes[m].writes[pwi].layout,
|
||||
.newLayout = node->reads[ri].layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = VK_NULL_HANDLE,
|
||||
.subresourceRange = color_range,
|
||||
};
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graph->compiled = true;
|
||||
}
|
||||
|
||||
VkResult render_graph_execute(
|
||||
RenderGraph* graph,
|
||||
VkCommandBuffer graphics_cmd,
|
||||
VkCommandBuffer compute_cmd,
|
||||
RenderContext* gpu,
|
||||
UIContext* ui,
|
||||
HexContext* hex,
|
||||
uint32_t image_index,
|
||||
double time) {
|
||||
assert(graph->compiled && "render_graph_compile must be called before execute");
|
||||
|
||||
uint32_t frame = gpu->current_frame;
|
||||
|
||||
for(uint32_t ti = 0; ti < graph->topo_count; ti++) {
|
||||
uint32_t n = graph->topo_order[ti];
|
||||
RGNode* node = &graph->nodes[n];
|
||||
|
||||
VkCommandBuffer cmd = (node->type == RG_NODE_COMPUTE) ? compute_cmd : graphics_cmd;
|
||||
|
||||
// Emit pre-barriers, resolving the per-frame image for each one.
|
||||
for(uint32_t bi = 0; bi < node->pre_barrier_count; bi++) {
|
||||
RGBarrier* bar = &node->pre_barriers[bi];
|
||||
VkImageMemoryBarrier img_barrier = bar->barrier;
|
||||
|
||||
uint32_t slot = bar->resource[frame];
|
||||
if(slot == RG_RESOURCE_SWAPCHAIN) {
|
||||
img_barrier.image = gpu->swapchain_images[image_index];
|
||||
} else {
|
||||
img_barrier.image = ui->texture_slots[slot].image;
|
||||
}
|
||||
|
||||
vkCmdPipelineBarrier(cmd,
|
||||
bar->src_stage, bar->dst_stage,
|
||||
0, 0, NULL, 0, NULL,
|
||||
1, &img_barrier);
|
||||
}
|
||||
|
||||
if(node->record != NULL) {
|
||||
node->record(cmd, node->userdata, gpu, ui, hex, image_index, frame, time);
|
||||
}
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
void render_graph_invalidate(RenderGraph* graph) {
|
||||
graph->compiled = false;
|
||||
}
|
||||
Loading…
Reference in New Issue