diff options
author | omnisci3nce <omniscient.oce@gmail.com> | 2024-07-14 21:47:25 +1000 |
---|---|---|
committer | omnisci3nce <omniscient.oce@gmail.com> | 2024-07-14 21:47:25 +1000 |
commit | 529a603128d5e9dc4701322f44961f165e2183e1 (patch) | |
tree | 3e5d65ac503b971412ae35bfc5fb67a438a3c364 /src/ral | |
parent | 5b001d39d42314085164724d3a417fb8ebd54f98 (diff) |
generate api docs python
Diffstat (limited to 'src/ral')
-rw-r--r-- | src/ral/backends/opengl/backend_opengl.c | 284 | ||||
-rw-r--r-- | src/ral/backends/opengl/backend_opengl.h | 6 | ||||
-rw-r--r-- | src/ral/ral_common.c | 46 | ||||
-rw-r--r-- | src/ral/ral_common.h | 20 | ||||
-rw-r--r-- | src/ral/ral_impl.h | 56 | ||||
-rw-r--r-- | src/ral/ral_types.h | 5 |
6 files changed, 395 insertions, 22 deletions
diff --git a/src/ral/backends/opengl/backend_opengl.c b/src/ral/backends/opengl/backend_opengl.c new file mode 100644 index 0000000..2c7c411 --- /dev/null +++ b/src/ral/backends/opengl/backend_opengl.c @@ -0,0 +1,284 @@ +#include <assert.h> +#include "backend_opengl.h" +#include "log.h" +#include "mem.h" +#include "ral_common.h" +#include "ral_impl.h" +#include "ral_types.h" + +#include <glad/glad.h> +#include <glfw3.h> + +typedef struct OpenglCtx { + GLFWwindow* window; + arena pool_arena; + GPU_CmdBuffer main_cmd_buffer; + GPU_BackendPools gpu_pools; + ResourcePools* resource_pools; +} OpenglCtx; + +static OpenglCtx context; + +bool GPU_Backend_Init(const char* window_name, struct GLFWwindow *window, struct ResourcePools* res_pools) { + INFO("loading OpenGL backend"); + + memset(&context, 0, sizeof(context)); + context.window = window; + + size_t pool_buffer_size = 1024 * 1024; + context.pool_arena = arena_create(malloc(pool_buffer_size), pool_buffer_size); + + BackendPools_Init(&context.pool_arena, &context.gpu_pools); + context.resource_pools = res_pools; + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + + // glad: load all opengl function pointers + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { + ERROR("Failed to initialise GLAD \n"); + return false; + } + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + return true; +} + +// All of these are no-ops in OpenGL +void GPU_Backend_Shutdown() {} +bool GPU_Device_Create(GPU_Device* out_device) { return true; } +void GPU_Device_Destroy(GPU_Device* device) {} +bool GPU_Swapchain_Create(GPU_Swapchain* out_swapchain) { return true; } +void GPU_Swapchain_Destroy(GPU_Swapchain* swapchain) {} +void GPU_CmdEncoder_Destroy(GPU_CmdEncoder* encoder) {} +void GPU_CmdEncoder_BeginRender(GPU_CmdEncoder* encoder, GPU_Renderpass* renderpass) {} +void GPU_CmdEncoder_EndRender(GPU_CmdEncoder* encoder) {} +GPU_CmdEncoder* GPU_GetDefaultEncoder() { + return context. +} +void GPU_QueueSubmit(GPU_CmdBuffer* cmd_buffer) {} + +GPU_Renderpass* GPU_Renderpass_Create(GPU_RenderpassDesc description) { + // allocate new pass + GPU_Renderpass* renderpass = Renderpass_pool_alloc(&context.gpu_pools.renderpasses, NULL); + renderpass->description = description; + + if (!description.default_framebuffer) { + // If we're not using the default framebuffer we need to generate a new one + GLuint gl_fbo_id; + glGenFramebuffers(1, &gl_fbo_id); + renderpass->fbo = gl_fbo_id; + } else { + renderpass->fbo = OPENGL_DEFAULT_FRAMEBUFFER; + assert(!description.has_color_target); + assert(!description.has_depth_stencil); + } + glBindFramebuffer(GL_FRAMEBUFFER, renderpass->fbo); + + if (description.has_color_target && !description.default_framebuffer) { + GPU_Texture* colour_attachment = TEXTURE_GET(description.color_target); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + colour_attachment->id, 0); + } + if (description.has_depth_stencil && !description.default_framebuffer) { + GPU_Texture* depth_attachment = TEXTURE_GET(description.depth_stencil); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_attachment->id, + 0); + } + + if (description.has_depth_stencil && !description.has_color_target) { + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); // reset to default framebuffer + + return renderpass; +} + +void GPU_Renderpass_Destroy(GPU_Renderpass* pass) { glDeleteFramebuffers(1, &pass->fbo); } + +GPU_Pipeline* GPU_GraphicsPipeline_Create(GraphicsPipelineDesc description, GPU_Renderpass* renderpass) { + GPU_Pipeline* pipeline = Pipeline_pool_alloc(&context.gpu_pools.pipelines, NULL); + + // Create shader program + u32 shader_id = shader_create_separate(description.vs.filepath.buf, description.fs.filepath.buf); + pipeline->shader_id = shader_id; + + // Vertex format + pipeline->vertex_desc = description.vertex_desc; + + // Allocate uniform buffers if needed + u32 ubo_count = 0; + // printf("data layouts %d\n", description.data_layouts_count); + for (u32 layout_i = 0; layout_i < description.data_layouts_count; layout_i++) { + ShaderDataLayout sdl = description.data_layouts[layout_i].get_layout(NULL); + TRACE("Got shader data layout %d's bindings! . found %d", layout_i, sdl.binding_count); + + for (u32 binding_j = 0; binding_j < sdl.binding_count; binding_j++) { + u32 binding_id = binding_j; + assert(binding_id < MAX_PIPELINE_UNIFORM_BUFFERS); + ShaderBinding binding = sdl.bindings[binding_j]; + // Do I want Buffer vs Bytes? + if (binding.kind == BINDING_BUFFER) { + static u32 s_binding_point = 0; + BufferHandle ubo_handle = + GPU_BufferCreate(binding.data.bytes.size, BUFFER_UNIFORM, BUFFER_FLAG_GPU, NULL); // no data right now + pipeline->uniform_bindings[ubo_count++] = ubo_handle; + GPU_Buffer* ubo_buf = BUFFER_GET(ubo_handle); + + i32 blockIndex = glGetUniformBlockIndex(pipeline->shader_id, binding.label); + printf("Block index for %s: %d", binding.label, blockIndex); + if (blockIndex < 0) { + WARN("Couldn't retrieve block index for uniform block '%s'", binding.label); + } else { + // DEBUG("Retrived block index %d for %s", blockIndex, binding.label); + } + u32 blocksize; + glGetActiveUniformBlockiv(pipeline->shader_id, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, + &blocksize); + printf("\t with size %d bytes\n", blocksize); + + glBindBufferBase(GL_UNIFORM_BUFFER, s_binding_point, ubo_buf->id.ubo); + if (blockIndex != GL_INVALID_INDEX) { + glUniformBlockBinding(pipeline->shader_id, blockIndex, s_binding_point); + } + ubo_buf->ubo_binding_point = s_binding_point++; + ubo_buf->name = binding.label; + assert(s_binding_point < GL_MAX_UNIFORM_BUFFER_BINDINGS); + } + } + } + pipeline->uniform_count = ubo_count; + + pipeline->renderpass = description.renderpass; + pipeline->wireframe = description.wireframe; + + return pipeline; +} + +void GraphicsPipeline_Destroy(GPU_Pipeline* pipeline) {} + +GPU_CmdEncoder GPU_CmdEncoder_Create() { + GPU_CmdEncoder encoder = { 0 }; + return encoder; +} + + +BufferHandle GPU_BufferCreate(u64 size, GPU_BufferType buf_type, GPU_BufferFlags flags, const void *data) { + // "allocating" the cpu-side buffer struct + BufferHandle handle; + GPU_Buffer* buffer = Buffer_pool_alloc(&context.resource_pools, &handle); + buffer->size = size; + buffer->vao = 0; + + // Opengl buffer + GLuint gl_buffer_id; + glGenBuffers(1, &gl_buffer_id); + + GLenum gl_buf_type; + GLenum gl_buf_usage = GL_STATIC_DRAW; + + switch (buf_type) { + case BUFFER_UNIFORM: + DEBUG("Creating Uniform buffer"); + gl_buf_type = GL_UNIFORM_BUFFER; + /* gl_buf_usage = GL_DYNAMIC_DRAW; */ + buffer->id.ubo = gl_buffer_id; + break; + case BUFFER_DEFAULT: + case BUFFER_VERTEX: + DEBUG("Creating Vertex buffer"); + gl_buf_type = GL_ARRAY_BUFFER; + buffer->id.vbo = gl_buffer_id; + break; + case BUFFER_INDEX: + DEBUG("Creating Index buffer"); + gl_buf_type = GL_ELEMENT_ARRAY_BUFFER; + buffer->id.ibo = gl_buffer_id; + break; + default: + WARN("Unimplemented gpu_buffer_type provided %s", buffer_type_names[buf_type]); + break; + } + // bind buffer + glBindBuffer(gl_buf_type, gl_buffer_id); + + if (data) { + TRACE("Upload data (%d bytes) as part of buffer creation", size); + glBufferData(gl_buf_type, buffer->size, data, gl_buf_usage); + } else { + TRACE("Allocating but not uploading (%d bytes)", size); + glBufferData(gl_buf_type, buffer->size, NULL, gl_buf_usage); + } + + glBindBuffer(gl_buf_type, 0); + + return handle; +} + +TextureHandle GPU_TextureCreate(TextureDesc desc, bool create_view, const void *data) { + // "allocating" the cpu-side struct + TextureHandle handle; + GPU_Texture* texture = Texture_pool_alloc(&context.resource_pools->textures, &handle); + DEBUG("Allocated texture with handle %d", handle.raw); + + GLuint gl_texture_id; + glGenTextures(1, &gl_texture_id); + texture->id = gl_texture_id; + + glBindTexture(GL_TEXTURE_2D, gl_texture_id); + + GLint internal_format = + desc.format == TEXTURE_FORMAT_DEPTH_DEFAULT ? GL_DEPTH_COMPONENT : GL_RGB; + GLenum format = desc.format == TEXTURE_FORMAT_DEPTH_DEFAULT ? GL_DEPTH_COMPONENT : GL_RGBA; + GLenum data_type = desc.format == TEXTURE_FORMAT_DEPTH_DEFAULT ? GL_FLOAT : GL_UNSIGNED_BYTE; + + if (desc.format == TEXTURE_FORMAT_DEPTH_DEFAULT) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + } else { + // set the texture wrapping parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + // set texture filtering parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + + if (data) { + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, desc.extents.x, desc.extents.y, 0, format, + data_type, data); + glGenerateMipmap(GL_TEXTURE_2D); + } else { + WARN("No image data provided"); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, desc.extents.x, desc.extents.y, 0, format, + data_type, NULL); + } + + glBindTexture(GL_TEXTURE_2D, 0); + + return handle; +} + +void GPU_TextureDestroy(TextureHandle handle) { + glDeleteTextures(1, &handle.raw); +} + +// TODO: void GPU_TextureUpload(TextureHandle handle, size_t n_bytes, const void* data) + +bool GPU_Backend_BeginFrame() { + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void GPU_Backend_EndFrame() { + glfwSwapBuffers(context.window); +} diff --git a/src/ral/backends/opengl/backend_opengl.h b/src/ral/backends/opengl/backend_opengl.h index 22162f3..4fbb4bb 100644 --- a/src/ral/backends/opengl/backend_opengl.h +++ b/src/ral/backends/opengl/backend_opengl.h @@ -39,11 +39,11 @@ typedef struct GPU_Renderpass { typedef struct GPU_CmdEncoder { GPU_Pipeline *pipeline; -} GPU_CmdEncoder; // Recording +} GPU_CmdEncoder; // Recording -typedef struct gpu_cmd_buffer { +typedef struct GPU_CmdBuffer { void *pad; -} gpu_cmd_buffer; // Ready for submission +} GPU_CmdBuffer; // Ready for submission typedef struct GPU_Buffer { union { diff --git a/src/ral/ral_common.c b/src/ral/ral_common.c index 89d475b..8ff282e 100644 --- a/src/ral/ral_common.c +++ b/src/ral/ral_common.c @@ -1,7 +1,7 @@ #include "ral_common.h" #include "ral_impl.h" -void backend_pools_init(arena* a, GPU_BackendPools* backend_pools) { +void BackendPools_Init(arena* a, GPU_BackendPools* backend_pools) { PipelineLayout_pool pipeline_layout_pool = PipelineLayout_pool_create(a, MAX_PIPELINES, sizeof(GPU_PipelineLayout)); backend_pools->pipeline_layouts = pipeline_layout_pool; @@ -11,9 +11,51 @@ void backend_pools_init(arena* a, GPU_BackendPools* backend_pools) { backend_pools->renderpasses = rpass_pool; } -void resource_pools_init(arena* a, struct ResourcePools* res_pools) { +void ResourcePools_Init(arena* a, struct ResourcePools* res_pools) { Buffer_pool buf_pool = Buffer_pool_create(a, MAX_BUFFERS, sizeof(GPU_Buffer)); res_pools->buffers = buf_pool; Texture_pool tex_pool = Texture_pool_create(a, MAX_TEXTURES, sizeof(GPU_Texture)); res_pools->textures = tex_pool; } + +VertexDescription static_3d_vertex_description() { + VertexDescription builder = { .debug_label = "Standard static 3d vertex format" }; + VertexDesc_AddAttr(&builder, "inPosition", ATTR_F32x3); + VertexDesc_AddAttr(&builder, "inNormal", ATTR_F32x3); + VertexDesc_AddAttr(&builder, "inTexCoords", ATTR_F32x2); + builder.use_full_vertex_size = true; + return builder; +} + +void VertexDesc_AddAttr(VertexDescription* builder, const char* name, VertexAttribType type) { + u32 i = builder->attributes_count; + + size_t size = VertexAttribSize(type); + builder->attributes[i] = type; + builder->stride += size; + builder->attr_names[i] = name; + + builder->attributes_count++; +} + +size_t VertexAttribSize(VertexAttribType attr) { + switch (attr) { + case ATTR_F32: + case ATTR_U32: + case ATTR_I32: + return 4; + case ATTR_F32x2: + case ATTR_U32x2: + case ATTR_I32x2: + return 8; + case ATTR_F32x3: + case ATTR_U32x3: + case ATTR_I32x3: + return 12; + case ATTR_F32x4: + case ATTR_U32x4: + case ATTR_I32x4: + return 16; + break; + } +} diff --git a/src/ral/ral_common.h b/src/ral/ral_common.h index 1088404..0f7c1b7 100644 --- a/src/ral/ral_common.h +++ b/src/ral/ral_common.h @@ -1,3 +1,6 @@ +/** + * @brief Common functions that don't actually depend on the specific backend +*/ #pragma once #include "defines.h" #include "buf.h" @@ -5,7 +8,6 @@ #include "ral_types.h" #include "ral_impl.h" - TYPED_POOL(GPU_Buffer, Buffer); TYPED_POOL(GPU_Texture, Texture); TYPED_POOL(GPU_PipelineLayout, PipelineLayout); @@ -17,23 +19,23 @@ TYPED_POOL(GPU_Renderpass, Renderpass); #define TEXTURE_GET(h) (texture_pool_get(&context.resource_pools->textures, h)) // --- Pools -typedef struct GPU_BackendPools{ +typedef struct GPU_BackendPools { Pipeline_pool pipelines; PipelineLayout_pool pipeline_layouts; Renderpass_pool renderpasses; } GPU_BackendPools; -void backend_pools_init(arena* a, GPU_BackendPools* backend_pools); +void BackendPools_Init(arena* a, GPU_BackendPools* backend_pools); struct ResourcePools { Buffer_pool buffers; Texture_pool textures; }; -void resource_pools_init(arena* a, struct ResourcePools* res_pools); - +typedef struct ResourcePools ResourcePools; +void ResourcePools_Init(arena* a, struct ResourcePools* res_pools); // --- Vertex formats -bytebuffer vertices_as_bytebuffer(arena* a, VertexFormat format, Vertex_darray* vertices); - -void vertex_desc_add(VertexDescription* builder, const char* name, VertexAttribType type); VertexDescription static_3d_vertex_description(); -size_t vertex_attrib_size(VertexAttribType attr); + +void VertexDesc_AddAttr(VertexDescription* builder, const char* name, VertexAttribType type); + +size_t VertexAttribSize(VertexAttribType attr); diff --git a/src/ral/ral_impl.h b/src/ral/ral_impl.h index 4d1c17a..a896eff 100644 --- a/src/ral/ral_impl.h +++ b/src/ral/ral_impl.h @@ -2,7 +2,9 @@ * @brief */ #pragma once +#include "buf.h" #include "defines.h" +#include "ral_common.h" #include "ral_types.h" struct GLFWwindow; @@ -18,7 +20,9 @@ typedef struct GPU_CmdBuffer GPU_CmdBuffer; // Ready for submission typedef struct GPU_Buffer GPU_Buffer; typedef struct GPU_Texture GPU_Texture; -bool GPU_Backend_Init(const char* window_name, struct GLFWwindow* window); +struct ResourcePools; + +bool GPU_Backend_Init(const char* window_name, struct GLFWwindow* window, struct ResourcePools* res_pools); void GPU_Backend_Shutdown(); bool GPU_Device_Create(GPU_Device* out_device); @@ -27,12 +31,54 @@ void GPU_Device_Destroy(GPU_Device* device); bool GPU_Swapchain_Create(GPU_Swapchain* out_swapchain); void GPU_Swapchain_Destroy(GPU_Swapchain* swapchain); -GPU_Renderpass* GPU_Renderpass_Create(GPU_RenderpassDesc description); -void GPU_Renderpass_Destroy(GPU_Renderpass* pass); +PUB GPU_Renderpass* GPU_Renderpass_Create(GPU_RenderpassDesc description); +PUB void GPU_Renderpass_Destroy(GPU_Renderpass* pass); + +PUB GPU_Pipeline* GPU_GraphicsPipeline_Create(GraphicsPipelineDesc description, GPU_Renderpass* renderpass); +PUB void GraphicsPipeline_Destroy(GPU_Pipeline* pipeline); + +// --- Command buffer +PUB GPU_CmdEncoder GPU_CmdEncoder_Create(); +PUB void GPU_CmdEncoder_Destroy(GPU_CmdEncoder* encoder); +PUB void GPU_CmdEncoder_BeginRender(GPU_CmdEncoder* encoder, GPU_Renderpass* renderpass); +PUB void GPU_CmdEncoder_EndRender(GPU_CmdEncoder* encoder); +PUB GPU_CmdEncoder* GPU_GetDefaultEncoder(); +PUB void GPU_QueueSubmit(GPU_CmdBuffer* cmd_buffer); + +// --- Buffers +PUB BufferHandle GPU_BufferCreate(u64 size, GPU_BufferType buf_type, GPU_BufferFlags flags, const void* data); +PUB void GPU_BufferDestroy(BufferHandle handle); +PUB void GPU_BufferUpload(BufferHandle buffer, size_t n_bytes, const void* data); + +// --- Textures +PUB TextureHandle GPU_TextureCreate(TextureDesc desc, bool create_view, const void* data); +PUB void GPU_TextureDestroy(TextureHandle handle); +PUB void GPU_TextureUpload(TextureHandle handle, size_t n_bytes, const void* data); + +// --- Data copy commands +// TODO: Rename these to reflect current coding style +void encode_buffer_copy(GPU_CmdEncoder* encoder, BufferHandle src, u64 src_offset, + BufferHandle dst, u64 dst_offset, u64 copy_size); +void buffer_upload_bytes(BufferHandle gpu_buf, bytebuffer cpu_buf, u64 offset, u64 size); + +void copy_buffer_to_buffer_oneshot(BufferHandle src, u64 src_offset, BufferHandle dst, + u64 dst_offset, u64 copy_size); +void copy_buffer_to_image_oneshot(BufferHandle src, TextureHandle dst); + +// --- Render commands +PUB void GPU_EncodeBindPipeline(GPU_CmdEncoder* encoder, GPU_Pipeline* pipeline); +PUB void GPU_EncodeBindShaderData(GPU_CmdEncoder* encoder, u32 group, ShaderData* data); +void GPU_EncodeSetDefaults(GPU_CmdEncoder* encoder); +PUB void GPU_EncodeSetVertexBuffer(GPU_CmdEncoder* encoder, BufferHandle buf); +PUB void GPU_EncodeSetIndexBuffer(GPU_CmdEncoder* encoder, BufferHandle buf); +PUB void GPU_EncodeDraw(GPU_CmdEncoder* encoder, u64 count); +PUB void GPU_EncodeDrawIndexed(GPU_CmdEncoder* encoder, u64 index_count); -GPU_Pipeline* GPU_GraphicsPipeline_Create(GraphicsPipelineDesc description, GPU_Renderpass* renderpass); -void GraphicsPipeline_Destroy(GPU_Pipeline* pipeline); +// --- Frame cycle +PUB bool GPU_Backend_BeginFrame(); +PUB void GPU_Backend_EndFrame(); +// Concrete implementation #if defined(CEL_REND_BACKEND_OPENGL) #include "backend_opengl.h" #endif diff --git a/src/ral/ral_types.h b/src/ral/ral_types.h index 0ba7f87..188951a 100644 --- a/src/ral/ral_types.h +++ b/src/ral/ral_types.h @@ -176,7 +176,7 @@ typedef struct ShaderDataLayout { size_t binding_count; } ShaderDataLayout; -typedef ShaderDataLayout (*FN_GetBindingLayout)(void); +typedef ShaderDataLayout (*FN_GetBindingLayout)(void* data); typedef struct ShaderData { FN_GetBindingLayout get_layout; @@ -197,7 +197,6 @@ typedef enum PrimitiveTopology { typedef enum CullMode { CULL_BACK_FACE, CULL_FRONT_FACE, CULL_COUNT } CullMode; typedef struct GraphicsPipelineDesc { - // GPU_Renderpass* renderpass -> takes a renderpass in the create function const char* debug_name; VertexDescription vertex_desc; ShaderDesc vs; /** @brief Vertex shader stage */ @@ -205,7 +204,7 @@ typedef struct GraphicsPipelineDesc { // Roughly equivalent to a descriptor set layout each. each layout can have multiple bindings // examples: - // - uniform buffer reprensenting view projection matrix + // - uniform buffer representing view projection matrix // - texture for shadow map ShaderData data_layouts[MAX_SHADER_DATA_LAYOUTS]; u32 data_layouts_count; |