Skip to content

Commit

Permalink
Implement Mesh Shader support for Rendering Device Vulkan and DirectX12
Browse files Browse the repository at this point in the history
  • Loading branch information
thimenesup committed Dec 5, 2024
1 parent 0eadbdb commit a940517
Show file tree
Hide file tree
Showing 20 changed files with 536 additions and 8 deletions.
12 changes: 12 additions & 0 deletions doc/classes/RDShaderSPIRV.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@
<member name="bytecode_fragment" type="PackedByteArray" setter="set_stage_bytecode" getter="get_stage_bytecode" default="PackedByteArray()">
The SPIR-V bytecode for the fragment shader stage.
</member>
<member name="bytecode_mesh" type="PackedByteArray" setter="set_stage_bytecode" getter="get_stage_bytecode" default="PackedByteArray()">
The SPIR-V bytecode for the mesh shader stage.
</member>
<member name="bytecode_mesh_task" type="PackedByteArray" setter="set_stage_bytecode" getter="get_stage_bytecode" default="PackedByteArray()">
The SPIR-V bytecode for the mesh task shader stage.
</member>
<member name="bytecode_tesselation_control" type="PackedByteArray" setter="set_stage_bytecode" getter="get_stage_bytecode" default="PackedByteArray()">
The SPIR-V bytecode for the tessellation control shader stage.
</member>
Expand All @@ -63,6 +69,12 @@
<member name="compile_error_fragment" type="String" setter="set_stage_compile_error" getter="get_stage_compile_error" default="&quot;&quot;">
The compilation error message for the fragment shader stage (set by the SPIR-V compiler and Godot). If empty, shader compilation was successful.
</member>
<member name="compile_error_mesh" type="String" setter="set_stage_compile_error" getter="get_stage_compile_error" default="&quot;&quot;">
The compilation error message for the mesh shader stage (set by the SPIR-V compiler and Godot). If empty, shader compilation was successful.
</member>
<member name="compile_error_mesh_task" type="String" setter="set_stage_compile_error" getter="get_stage_compile_error" default="&quot;&quot;">
The compilation error message for the mesh task shader stage (set by the SPIR-V compiler and Godot). If empty, shader compilation was successful.
</member>
<member name="compile_error_tesselation_control" type="String" setter="set_stage_compile_error" getter="get_stage_compile_error" default="&quot;&quot;">
The compilation error message for the tessellation control shader stage (set by the SPIR-V compiler and Godot). If empty, shader compilation was successful.
</member>
Expand Down
6 changes: 6 additions & 0 deletions doc/classes/RDShaderSource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
<member name="source_fragment" type="String" setter="set_stage_source" getter="get_stage_source" default="&quot;&quot;">
Source code for the shader's fragment stage.
</member>
<member name="source_mesh" type="String" setter="set_stage_source" getter="get_stage_source" default="&quot;&quot;">
Source code for the shader's mesh stage.
</member>
<member name="source_mesh_task" type="String" setter="set_stage_source" getter="get_stage_source" default="&quot;&quot;">
Source code for the shader's mesh task stage.
</member>
<member name="source_tesselation_control" type="String" setter="set_stage_source" getter="get_stage_source" default="&quot;&quot;">
Source code for the shader's tessellation control stage.
</member>
Expand Down
61 changes: 60 additions & 1 deletion doc/classes/RenderingDevice.xml
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,25 @@
Removes and disables the scissor rectangle for the specified [param draw_list]. See also [method draw_list_enable_scissor].
</description>
</method>
<method name="draw_list_dispatch_mesh">
<return type="void" />
<param index="0" name="draw_list" type="int" />
<param index="1" name="x_groups" type="int" />
<param index="2" name="y_groups" type="int" />
<param index="3" name="z_groups" type="int" />
<description>
Submits [param draw_list] for rendering on the GPU, using bound render pipeline with mesh task or mesh shader. You must ensure that the GPU supports this functionality by calling [method has_feature] with [constant SUPPORTS_MESH_SHADER] as a parameter.
</description>
</method>
<method name="draw_list_dispatch_mesh_indirect">
<return type="void" />
<param index="0" name="draw_list" type="int" />
<param index="1" name="buffer" type="RID" />
<param index="2" name="offset" type="int" />
<description>
Submits [param draw_list] for rendering on the GPU with the given group counts stored in the [param buffer] at [param offset]. Buffer must have been created with [constant STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT] flag. Equivalent to [method draw_list_dispatch_mesh].
</description>
</method>
<method name="draw_list_draw">
<return type="void" />
<param index="0" name="draw_list" type="int" />
Expand Down Expand Up @@ -651,6 +670,13 @@
This is only used by Vulkan in debug builds. Godot must also be started with the [code]--extra-gpu-memory-tracking[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
</description>
</method>
<method name="has_feature" qualifiers="const">
<return type="bool" />
<param index="0" name="feature" type="int" enum="RenderingDevice.Features" />
<description>
Returns [code]true[/code] if the [param feature] is supported by the GPU.
</description>
</method>
<method name="index_array_create">
<return type="RID" />
<param index="0" name="index_buffer" type="RID" />
Expand Down Expand Up @@ -2344,7 +2370,13 @@
<constant name="SHADER_STAGE_COMPUTE" value="4" enum="ShaderStage">
Compute shader stage. This can be used to run arbitrary computing tasks in a shader, performing them on the GPU instead of the CPU.
</constant>
<constant name="SHADER_STAGE_MAX" value="5" enum="ShaderStage">
<constant name="SHADER_STAGE_MESH_TASK" value="5" enum="ShaderStage">
Mesh task shader stage. This optional stage can be used to dispatch any arbitrary amount of mesh shaders.
</constant>
<constant name="SHADER_STAGE_MESH" value="6" enum="ShaderStage">
Mesh shader stage. This can be used to draw meshes where geometry has been entirely generated within the shader.
</constant>
<constant name="SHADER_STAGE_MAX" value="7" enum="ShaderStage">
Represents the size of the [enum ShaderStage] enum.
</constant>
<constant name="SHADER_STAGE_VERTEX_BIT" value="1" enum="ShaderStage">
Expand All @@ -2362,6 +2394,12 @@
<constant name="SHADER_STAGE_COMPUTE_BIT" value="16" enum="ShaderStage">
Compute shader stage bit (see also [constant SHADER_STAGE_COMPUTE]).
</constant>
<constant name="SHADER_STAGE_MESH_TASK_BIT" value="32" enum="ShaderStage">
Mesh task shader stage bit (see also [constant SHADER_STAGE_MESH_TASK]).
</constant>
<constant name="SHADER_STAGE_MESH_BIT" value="64" enum="ShaderStage">
Mesh shader stage bit (see also [constant SHADER_STAGE_MESH]).
</constant>
<constant name="SHADER_LANGUAGE_GLSL" value="0" enum="ShaderLanguage">
Khronos' GLSL shading language (used natively by OpenGL and Vulkan). This is the language used for core Godot shaders.
</constant>
Expand All @@ -2377,6 +2415,9 @@
<constant name="PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT" value="2" enum="PipelineSpecializationConstantType">
Floating-point specialization constant.
</constant>
<constant name="SUPPORTS_MESH_SHADER" value="4" enum="Features">
Features support for mesh and mesh task shader extension.
</constant>
<constant name="LIMIT_MAX_BOUND_UNIFORM_SETS" value="0" enum="Limit">
Maximum number of uniform sets that can be bound at a given time.
</constant>
Expand Down Expand Up @@ -2488,6 +2529,24 @@
<constant name="LIMIT_MAX_VIEWPORT_DIMENSIONS_Y" value="36" enum="Limit">
Maximum viewport height (in pixels).
</constant>
<constant name="LIMIT_MAX_MESH_TASK_WORKGROUP_COUNT_X" value="37" enum="Limit">
Maximum workgroup size for mesh task shaders on the X axis.
</constant>
<constant name="LIMIT_MAX_MESH_TASK_WORKGROUP_COUNT_Y" value="38" enum="Limit">
Maximum workgroup size for mesh task shaders on the Y axis.
</constant>
<constant name="LIMIT_MAX_MESH_TASK_WORKGROUP_COUNT_Z" value="39" enum="Limit">
Maximum workgroup size for mesh task shaders on the Z axis.
</constant>
<constant name="LIMIT_MAX_MESH_WORKGROUP_COUNT_X" value="40" enum="Limit">
Maximum workgroup size for mesh shaders on the X axis.
</constant>
<constant name="LIMIT_MAX_MESH_WORKGROUP_COUNT_Y" value="41" enum="Limit">
Maximum workgroup size for mesh shaders on the Y axis.
</constant>
<constant name="LIMIT_MAX_MESH_WORKGROUP_COUNT_Z" value="42" enum="Limit">
Maximum workgroup size for mesh shaders on the Z axis.
</constant>
<constant name="MEMORY_TEXTURES" value="0" enum="MemoryType">
Memory taken by textures.
</constant>
Expand Down
66 changes: 63 additions & 3 deletions drivers/d3d12/rendering_device_driver_d3d12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3095,6 +3095,8 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec
/* SHADER_STAGE_TESSELATION_CONTROL */ MESA_SHADER_TESS_CTRL,
/* SHADER_STAGE_TESSELATION_EVALUATION */ MESA_SHADER_TESS_EVAL,
/* SHADER_STAGE_COMPUTE */ MESA_SHADER_COMPUTE,
/* SHADER_STAGE_MESH_TASK */ MESA_SHADER_TASK,
/* SHADER_STAGE_MESH */ MESA_SHADER_MESH,
};

nir_shader *shader = spirv_to_nir(
Expand Down Expand Up @@ -3487,15 +3489,19 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec
D3D12_ROOT_SIGNATURE_FLAGS root_sig_flags =
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS;
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
if (!stages_processed.has_flag(SHADER_STAGE_VERTEX_BIT)) {
root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS;
}
if (!stages_processed.has_flag(SHADER_STAGE_FRAGMENT_BIT)) {
root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS;
}
if (!stages_processed.has_flag(SHADER_STAGE_MESH_TASK_BIT)) {
root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS;
}
if (!stages_processed.has_flag(SHADER_STAGE_MESH_BIT)) {
root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS;
}
if (binary_data.vertex_input_mask) {
root_sig_flags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
}
Expand Down Expand Up @@ -5416,6 +5422,29 @@ void RenderingDeviceDriverD3D12::command_render_draw_indirect_count(CommandBuffe
cmd_buf_info->cmd_list->ExecuteIndirect(indirect_cmd_signatures.draw.Get(), p_max_draw_count, indirect_buf_info->resource, p_offset, count_buf_info->resource, p_count_buffer_offset);
}

void RenderingDeviceDriverD3D12::command_render_dispatch_mesh(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) {
CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id;
((ID3D12GraphicsCommandList6 *)cmd_buf_info->cmd_list.Get())->DispatchMesh(p_x_groups, p_y_groups, p_z_groups);
}

void RenderingDeviceDriverD3D12::command_render_dispatch_mesh_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) {
CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id;
BufferInfo *indirect_buf_info = (BufferInfo *)p_indirect_buffer.id;
_resource_transition_batch(cmd_buf_info, indirect_buf_info, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT);
_resource_transitions_flush(cmd_buf_info);
cmd_buf_info->cmd_list->ExecuteIndirect(indirect_cmd_signatures.dispatch_mesh.Get(), p_draw_count, indirect_buf_info->resource, p_offset, nullptr, 0);
}

void RenderingDeviceDriverD3D12::command_render_dispatch_mesh_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) {
CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id;
BufferInfo *indirect_buf_info = (BufferInfo *)p_indirect_buffer.id;
BufferInfo *count_buf_info = (BufferInfo *)p_count_buffer.id;
_resource_transition_batch(cmd_buf_info, indirect_buf_info, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT);
_resource_transition_batch(cmd_buf_info, count_buf_info, 0, 1, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT);
_resource_transitions_flush(cmd_buf_info);
cmd_buf_info->cmd_list->ExecuteIndirect(indirect_cmd_signatures.dispatch_mesh.Get(), p_max_draw_count, indirect_buf_info->resource, p_offset, count_buf_info->resource, p_count_buffer_offset);
}

void RenderingDeviceDriverD3D12::command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) {
CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id;

Expand Down Expand Up @@ -6163,6 +6192,18 @@ uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) {
return D3D12_CS_THREAD_GROUP_MAX_Y;
case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z:
return D3D12_CS_THREAD_GROUP_MAX_Z;
case LIMIT_MAX_MESH_TASK_WORKGROUP_COUNT_X:
return MeshShaderCapabilities::MAX_THREAD_GROUPS;
case LIMIT_MAX_MESH_TASK_WORKGROUP_COUNT_Y:
return MeshShaderCapabilities::MAX_THREAD_GROUPS;
case LIMIT_MAX_MESH_TASK_WORKGROUP_COUNT_Z:
return MeshShaderCapabilities::MAX_THREAD_GROUPS;
case LIMIT_MAX_MESH_WORKGROUP_COUNT_X:
return MeshShaderCapabilities::MAX_THREAD_GROUPS;
case LIMIT_MAX_MESH_WORKGROUP_COUNT_Y:
return MeshShaderCapabilities::MAX_THREAD_GROUPS;
case LIMIT_MAX_MESH_WORKGROUP_COUNT_Z:
return MeshShaderCapabilities::MAX_THREAD_GROUPS;
case LIMIT_SUBGROUP_SIZE:
// Note in min/max. Shader model 6.6 supports it (see https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_6_WaveSize.html),
// but at this time I don't know the implications on the transpilation to DXIL, etc.
Expand Down Expand Up @@ -6221,6 +6262,8 @@ bool RenderingDeviceDriverD3D12::has_feature(Features p_feature) {
return vrs_capabilities.ss_image_supported;
case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS:
return true;
case SUPPORTS_MESH_SHADER:
return mesh_shader_capabilities.is_supported;
default:
return false;
}
Expand Down Expand Up @@ -6499,6 +6542,14 @@ Error RenderingDeviceDriverD3D12::_check_capabilities() {
}
}

D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = {};
res = device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, sizeof(options7));
if (SUCCEEDED(res)) {
if (options7.MeshShaderTier >= D3D12_MESH_SHADER_TIER_1) {
mesh_shader_capabilities.is_supported = true;
}
}

D3D12_FEATURE_DATA_D3D12_OPTIONS12 options12 = {};
res = device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &options12, sizeof(options12));
if (SUCCEEDED(res)) {
Expand Down Expand Up @@ -6546,6 +6597,12 @@ Error RenderingDeviceDriverD3D12::_check_capabilities() {
print_verbose("- Relaxed casting not supported");
}

if (mesh_shader_capabilities.is_supported) {
print_verbose("- D3D12 Mesh Shader supported");
} else {
print_verbose("- D3D12 Mesh Shader not supported");
}

print_verbose(String("- D3D12 16-bit ops supported: ") + (shader_capabilities.native_16bit_ops ? "yes" : "no"));

if (misc_features_support.depth_bounds_supported) {
Expand Down Expand Up @@ -6655,6 +6712,9 @@ Error RenderingDeviceDriverD3D12::_initialize_command_signatures() {
err = create_command_signature(device.Get(), D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH, sizeof(D3D12_DISPATCH_ARGUMENTS), &indirect_cmd_signatures.dispatch);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);

err = create_command_signature(device.Get(), D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_MESH, sizeof(D3D12_DISPATCH_MESH_ARGUMENTS), &indirect_cmd_signatures.dispatch_mesh);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);

return OK;
}

Expand Down
12 changes: 12 additions & 0 deletions drivers/d3d12/rendering_device_driver_d3d12.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
bool enhanced_barriers_supported = false;
};

struct MeshShaderCapabilities {
static const uint32_t MAX_THREAD_GROUPS = 63999; // Quoting the DirectX Mesh Shader Spec: "Each of the three thread group counts must be less than 64k" so ok...
bool is_supported = false;
};

struct MiscFeaturesSupport {
bool depth_bounds_supported = false;
};
Expand All @@ -162,6 +167,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
StorageBufferCapabilities storage_buffer_capabilities;
FormatCapabilities format_capabilities;
BarrierCapabilities barrier_capabilities;
MeshShaderCapabilities mesh_shader_capabilities;
MiscFeaturesSupport misc_features_support;
String pipeline_cache_id;

Expand Down Expand Up @@ -201,6 +207,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
ComPtr<ID3D12CommandSignature> draw;
ComPtr<ID3D12CommandSignature> draw_indexed;
ComPtr<ID3D12CommandSignature> dispatch;
ComPtr<ID3D12CommandSignature> dispatch_mesh;
} indirect_cmd_signatures;

static void STDMETHODCALLTYPE _debug_message_func(D3D12_MESSAGE_CATEGORY p_category, D3D12_MESSAGE_SEVERITY p_severity, D3D12_MESSAGE_ID p_id, LPCSTR p_description, void *p_context);
Expand Down Expand Up @@ -855,6 +862,11 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
virtual void command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
virtual void command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;

// Mesh Shader Drawing.
virtual void command_render_dispatch_mesh(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override final;
virtual void command_render_dispatch_mesh_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
virtual void command_render_dispatch_mesh_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;

// Buffer binding.
virtual void command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) override final;
virtual void command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) override final;
Expand Down
5 changes: 5 additions & 0 deletions drivers/metal/rendering_device_driver_metal.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,11 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) RenderingDeviceDriverMetal : public
virtual void command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
virtual void command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;

// Mesh Shader Drawing.
virtual void command_render_dispatch_mesh(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override final;
virtual void command_render_dispatch_mesh_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
virtual void command_render_dispatch_mesh_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;

// Buffer binding.
virtual void command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) override final;
virtual void command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) override final;
Expand Down
Loading

0 comments on commit a940517

Please sign in to comment.