diff options
| -rw-r--r-- | sokol_gfx.h | 145 | ||||
| -rw-r--r-- | util/sokol_gfx_imgui.h | 1 |
2 files changed, 111 insertions, 35 deletions
diff --git a/sokol_gfx.h b/sokol_gfx.h index 19a0642e..3b50eee2 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -2192,6 +2192,9 @@ typedef struct sg_features { bool compute; // storage buffers and compute shaders are supported bool msaa_texture_bindings; // if true, multisampled images can be bound as textures bool separate_buffer_types; // cannot use the same buffer for vertex and indices (only WebGL2) + bool draw_base_vertex; // draw with (base vertex > 0) && (base_instance == 0) supported + bool draw_base_instance; // draw with (base instance > 0) && (base_vertex == 0) supported + bool draw_base_vertex_base_instance; // draw with (base vertex > 0) && (base_instance > 0) supported bool gl_texture_views; // supports 'proper' texture views (GL 4.3+) } sg_features; @@ -3959,6 +3962,7 @@ typedef struct sg_trace_hooks { void (*apply_bindings)(const sg_bindings* bindings, void* user_data); void (*apply_uniforms)(int ub_index, const sg_range* data, void* user_data); void (*draw)(int base_element, int num_elements, int num_instances, void* user_data); + void (*draw_ex)(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance, void* user_data); void (*dispatch)(int num_groups_x, int num_groups_y, int num_groups_z, void* user_data); void (*end_pass)(void* user_data); void (*commit)(void* user_data); @@ -4233,6 +4237,7 @@ typedef struct sg_frame_stats { uint32_t num_apply_bindings; uint32_t num_apply_uniforms; uint32_t num_draw; + uint32_t num_draw_ex; uint32_t num_dispatch; uint32_t num_update_buffer; uint32_t num_append_buffer; @@ -4657,6 +4662,17 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_DRAW_BASEELEMENT, "sg_draw: base_element cannot be < 0") \ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_NUMELEMENTS, "sg_draw: num_elements cannot be < 0") \ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_NUMINSTANCES, "sg_draw: num_instances cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_RENDERPASS_EXPECTED, "sg_draw: must be called in a render pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEELEMENT, "sg_draw_ex: base_element cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_NUMELEMENTS, "sg_draw_ex: num_elements cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_NUMINSTANCES, "sg_draw_ex: num_instances cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEVERTEX, "sg_draw_ex: base_vertex cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEINSTANCE, "sg_draw_ex: base_instance cannot be < 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEVERTEX_VS_INDEXED, "sg_draw_ex(): base_vertex must be == 0 for non-indexed rendering") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEINSTANCE_VS_INSTANCED, "sg_draw_ex(): base_instance must be == 0 for non-instanced rendering") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEVERTEX_NOT_SUPPORTED, "sg_draw_ex(): base_vertex > 0 not supported on this backend (sg_features.draw_base_vertex)") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEINSTANCE_NOT_SUPPORTED, "sg_draw_ex(): base_instance > 0 not supported on this backend (sg_features.draw_base_instance)") \ + _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEVERTEXINSTANCE_NOT_SUPPORTED, "sg_draw_ex(): (base_vertex > 0) && (base_instance > 0) not supported on this backend (sg_features.draw_base_vertex_base_instance)") \ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING, "sg_draw: call to sg_apply_bindings() and/or sg_apply_uniforms() missing after sg_apply_pipeline()") \ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_COMPUTEPASS_EXPECTED, "sg_dispatch: must be called in a compute pass") \ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_NUMGROUPSX, "sg_dispatch: num_groups_x must be >=0 and <65536") \ @@ -4909,6 +4925,7 @@ SOKOL_GFX_API_DECL void sg_apply_pipeline(sg_pipeline pip); SOKOL_GFX_API_DECL void sg_apply_bindings(const sg_bindings* bindings); SOKOL_GFX_API_DECL void sg_apply_uniforms(int ub_slot, const sg_range* data); SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_instances); +SOKOL_GFX_API_DECL void sg_draw_ex(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance); SOKOL_GFX_API_DECL void sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z); SOKOL_GFX_API_DECL void sg_end_pass(void); SOKOL_GFX_API_DECL void sg_commit(void); @@ -6331,8 +6348,6 @@ typedef struct { bool valid; ID3D11Device* dev; ID3D11DeviceContext* ctx; - bool use_indexed_draw; - bool use_instanced_draw; struct { ID3D11RenderTargetView* render_view; ID3D11RenderTargetView* resolve_view; @@ -6671,7 +6686,6 @@ typedef struct { // the WGPU backend state typedef struct { bool valid; - bool use_indexed_draw; WGPUDevice dev; WGPULimits limits; WGPUQueue queue; @@ -6759,6 +6773,8 @@ typedef struct { } cur_pass; _sg_pipeline_ref_t cur_pip; bool next_draw_valid; + bool use_indexed_draw; + bool use_instanced_draw; uint32_t required_bindings_and_uniforms; // used to check that bindings and uniforms are applied after applying pipeline uint32_t applied_bindings_and_uniforms; // bits 0..7: uniform blocks, bit 8: bindings #if defined(SOKOL_DEBUG) @@ -7597,16 +7613,22 @@ _SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const s // FIXME: most of this isn't needed for compute pipelines const uint32_t required_bindings_flag = (1 << SG_MAX_UNIFORMBLOCK_BINDSLOTS); - for (int i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) { - const sg_vertex_attr_state* a_state = &desc->layout.attrs[i]; - if (a_state->format != SG_VERTEXFORMAT_INVALID) { - SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS); - cmn->vertex_buffer_layout_active[a_state->buffer_index] = true; + for (size_t attr_idx = 0; attr_idx < SG_MAX_VERTEX_ATTRIBUTES; attr_idx++) { + const sg_vertex_attr_state* attr_state = &desc->layout.attrs[attr_idx]; + if (attr_state->format != SG_VERTEXFORMAT_INVALID) { + SOKOL_ASSERT(attr_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS); + cmn->vertex_buffer_layout_active[attr_state->buffer_index] = true; cmn->required_bindings_and_uniforms |= required_bindings_flag; } } - cmn->is_compute = desc->compute; cmn->use_instanced_draw = false; + for (size_t vbuf_idx = 0; vbuf_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS; vbuf_idx++) { + const sg_vertex_buffer_layout_state* vbuf_state = &desc->layout.buffers[vbuf_idx]; + if (vbuf_state->step_func == SG_VERTEXSTEP_PER_INSTANCE) { + cmn->use_instanced_draw = true; + } + } + cmn->is_compute = desc->compute; cmn->shader = _sg_shader_ref(shd); cmn->layout = desc->layout; cmn->depth = desc->depth; @@ -10545,7 +10567,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, con gl_attr->divisor = 0; } else { gl_attr->divisor = (int8_t) step_rate; - pip->cmn.use_instanced_draw = true; } SOKOL_ASSERT(l_state->stride > 0); gl_attr->stride = (uint8_t) l_state->stride; @@ -11446,11 +11467,11 @@ _SOKOL_PRIVATE void _sg_gl_apply_uniforms(int ub_slot, const sg_range* data) { } _SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances) { - const GLenum i_type = _sg.gl.cache.cur_index_type; const GLenum p_type = _sg.gl.cache.cur_primitive_type; - const bool use_instanced_draw = (num_instances > 1) || _sg_pipeline_ref_ptr(&_sg.cur_pip)->cmn.use_instanced_draw; - if (0 != i_type) { + const bool use_instanced_draw = (num_instances > 1) || _sg.use_instanced_draw; + if (_sg.use_indexed_draw) { // indexed rendering + const GLenum i_type = _sg.gl.cache.cur_index_type; const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; const int ib_offset = _sg.gl.cache.cur_ib_offset; const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); @@ -13045,7 +13066,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { d3d11_comp->InstanceDataStepRate = (UINT)step_rate; - pip->cmn.use_instanced_draw = true; } } for (size_t layout_index = 0; layout_index < SG_MAX_VERTEXBUFFER_BINDSLOTS; layout_index++) { @@ -13584,9 +13604,6 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(shd->d3d11.vs); SOKOL_ASSERT(shd->d3d11.fs); - _sg.d3d11.use_indexed_draw = (pip->d3d11.index_format != DXGI_FORMAT_UNKNOWN); - _sg.d3d11.use_instanced_draw = pip->cmn.use_instanced_draw; - _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, (float*)&pip->cmn.blend_color, 0xFFFFFFFF); @@ -13757,8 +13774,8 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(int ub_slot, const sg_range* data) } _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { - const bool use_instanced_draw = (num_instances > 1) || (_sg.d3d11.use_instanced_draw); - if (_sg.d3d11.use_indexed_draw) { + const bool use_instanced_draw = (num_instances > 1) || (_sg.use_instanced_draw); + if (_sg.use_indexed_draw) { if (use_instanced_draw) { _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); _sg_stats_add(d3d11.draw.num_draw_indexed_instanced, 1); @@ -15107,10 +15124,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, co vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_state->stride; vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_state->step_func); vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_state->step_rate; - if (SG_VERTEXSTEP_PER_INSTANCE == l_state->step_func) { - // NOTE: not actually used in _sg_mtl_draw() - pip->cmn.use_instanced_draw = true; - } } } @@ -15895,7 +15908,7 @@ _SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_ins SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder); const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); SOKOL_ASSERT(pip); - if (SG_INDEXTYPE_NONE != pip->cmn.index_type) { + if (_sg.use_indexed_draw) { // indexed rendering const _sg_buffer_t* ib = _sg_buffer_ref_ptr(&_sg.mtl.cache.cur_ibuf); SOKOL_ASSERT(ib && (ib->mtl.buf[ib->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX)); @@ -17618,7 +17631,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, c } } else { WGPUVertexBufferLayout wgpu_vb_layouts[SG_MAX_VERTEXBUFFER_BINDSLOTS]; - _sg_clear(wgpu_vb_layouts, sizeof(wgpu_vb_layouts)); + _sg_clear(wgpu_vb_layouts, sieof(wgpu_vb_layouts)); WGPUVertexAttribute wgpu_vtx_attrs[SG_MAX_VERTEXBUFFER_BINDSLOTS][SG_MAX_VERTEX_ATTRIBUTES]; _sg_clear(wgpu_vtx_attrs, sizeof(wgpu_vtx_attrs)); int wgpu_vb_num = 0; @@ -17987,7 +18000,6 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(!_sg.cur_pass.is_compute); SOKOL_ASSERT(pip->wgpu.rpip); SOKOL_ASSERT(_sg.wgpu.rpass_enc); - _sg.wgpu.use_indexed_draw = (pip->cmn.index_type != SG_INDEXTYPE_NONE); wgpuRenderPassEncoderSetPipeline(_sg.wgpu.rpass_enc, pip->wgpu.rpip); wgpuRenderPassEncoderSetBlendConstant(_sg.wgpu.rpass_enc, &pip->wgpu.blend_color); wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.rpass_enc, pip->cmn.stencil.ref); @@ -18030,8 +18042,7 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(int ub_slot, const sg_range* data) { _SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances) { SOKOL_ASSERT(_sg.wgpu.rpass_enc); - const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip); - if (SG_INDEXTYPE_NONE != pip->cmn.index_type) { + if (_sg.use_indexed_draw) { wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.rpass_enc, (uint32_t)num_elements, (uint32_t)num_instances, (uint32_t)base_element, 0, 0); } else { wgpuRenderPassEncoderDraw(_sg.wgpu.rpass_enc, (uint32_t)num_elements, (uint32_t)num_instances, (uint32_t)base_element, 0); @@ -18423,7 +18434,7 @@ static inline void _sg_apply_uniforms(int ub_slot, const sg_range* data) { #endif } -static inline void _sg_draw(int base_element, int num_elements, int num_instances) { +static inline void _sg_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_index) { #if defined(_SOKOL_ANY_GL) _sg_gl_draw(base_element, num_elements, num_instances); #elif defined(SOKOL_METAL) @@ -19864,6 +19875,46 @@ _SOKOL_PRIVATE bool _sg_validate_draw(int base_element, int num_elements, int nu #endif } +_SOKOL_PRIVATE bool _sg_validate_draw_ex(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(base_element); + _SOKOL_UNUSED(num_elements); + _SOKOL_UNUSED(num_instances); + _SOKOL_UNUSED(base_vertex); + _SOKOL_UNUSED(base_instance); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pass.in_pass && !_sg.cur_pass.is_compute, VALIDATE_DRAW_EX_RENDERPASS_EXPECTED); + _SG_VALIDATE(base_element >= 0, VALIDATE_DRAW_EX_BASEELEMENT); + _SG_VALIDATE(num_elements >= 0, VALIDATE_DRAW_EX_NUMELEMENTS); + _SG_VALIDATE(num_instances >= 0, VALIDATE_DRAW_EX_NUMINSTANCES); + _SG_VALIDATE(base_vertex >= 0, VALIDATE_DRAW_EX_BASEVERTEX); + _SG_VALIDATE(base_instance >= 0, VALIDATE_DRAW_EX_BASEINSTANCE); + if (base_vertex > 0) { + _SG_VALIDATE(_sg.features.draw_base_vertex, VALIDATE_DRAW_EX_BASEVERTEX_NOT_SUPPORTED); + } + if (base_instance > 0) { + _SG_VALIDATE(_sg.features.draw_base_instance, VALIDATE_DRAW_EX_BASEINSTANCE_NOT_SUPPORTED); + } + if ((base_vertex > 0) && (base_instance > 0)) { + _SG_VALIDATE(_sg.features.draw_base_vertex_base_instance, VALIDATE_DRAW_EX_BASEVERTEXINSTANCE_NOT_SUPPORTED); + } + if (!_sg.use_indexed_draw) { + _SG_VALIDATE(base_vertex == 0, VALIDATE_DRAW_EX_BASEVERTEX_VS_INDEXED); + } + const bool use_instanced_draw = (num_instances > 1) || _sg.use_instanced_draw; + if (!use_instanced_draw) { + _SG_VALIDATE(base_instance == 0, VALIDATE_DRAW_EX_BASEINSTANCE_VS_INSTANCED); + } + _SG_VALIDATE(_sg.required_bindings_and_uniforms == _sg.applied_bindings_and_uniforms, VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING); + return _sg_validate_end(); + #endif +} + _SOKOL_PRIVATE bool _sg_validate_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(num_groups_x); @@ -21548,6 +21599,8 @@ SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { if (!_sg.next_draw_valid) { return; } + _sg.use_indexed_draw = pip->cmn.index_type != SG_INDEXTYPE_NONE; + _sg.use_instanced_draw = pip->cmn.use_instanced_draw; _sg_apply_pipeline(pip); @@ -21649,6 +21702,20 @@ SOKOL_API_IMPL void sg_apply_uniforms(int ub_slot, const sg_range* data) { _SG_TRACE_ARGS(apply_uniforms, ub_slot, data); } +_SOKOL_PRIVATE bool _sg_check_skip_draw(int num_elements, int num_instances) { + if (!_sg.cur_pass.valid) { + return true; + } + if (!_sg.next_draw_valid) { + return true; + } + // skip no-op draws + if ((0 == num_elements) || (0 == num_instances)) { + return true; + } + return false; +} + SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) { SOKOL_ASSERT(_sg.valid); #if defined(SOKOL_DEBUG) @@ -21657,18 +21724,26 @@ SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instance } #endif _sg_stats_add(num_draw, 1); - if (!_sg.cur_pass.valid) { + if (_sg_check_skip_draw(num_elements, num_instances)) { return; } - if (!_sg.next_draw_valid) { + _sg_draw(base_element, num_elements, num_instances, 0, 0); + _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances); +} + +SOKOL_API_IMPL void sg_draw_ex(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) { + SOKOL_ASSERT(_sg.valid); + #if defined(SOKOL_DEBUG) + if (!_sg_validate_draw_ex(base_element, num_elements, num_instances, base_vertex, base_instance)) { return; } - // skip no-op draws - if ((0 == num_elements) || (0 == num_instances)) { + #endif + _sg_stats_add(num_draw_ex, 1); + if (_sg_check_skip_draw(num_elements, num_instances)) { return; } - _sg_draw(base_element, num_elements, num_instances); - _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances); + _sg_draw(base_element, num_elements, num_instances, base_vertex, base_instance); + _SG_TRACE_ARGS(draw_ex, base_element, num_elements, num_instances, base_vertex, base_instance); } SOKOL_API_IMPL void sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) { diff --git a/util/sokol_gfx_imgui.h b/util/sokol_gfx_imgui.h index 9d52566f..b5153058 100644 --- a/util/sokol_gfx_imgui.h +++ b/util/sokol_gfx_imgui.h @@ -4519,6 +4519,7 @@ _SOKOL_PRIVATE void _sgimgui_draw_frame_stats_panel(sgimgui_t* ctx) { _sgimgui_frame_stats(num_apply_bindings); _sgimgui_frame_stats(num_apply_uniforms); _sgimgui_frame_stats(num_draw); + _sgimgui_frame_stats(num_draw_ex); _sgimgui_frame_stats(num_dispatch); _sgimgui_frame_stats(num_update_buffer); _sgimgui_frame_stats(num_append_buffer); |