/************************************************************************** * * Copyright 2012-2021 VMware, Inc. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * **************************************************************************/ /* * Draw.h -- * Functions that render 3D primitives. */ #include "Draw.h" #include "State.h" #include "Shader.h" #include "Debug.h" #include "util/u_draw.h" #include "util/u_memory.h" static unsigned ClampedUAdd(unsigned a, unsigned b) { unsigned c = a + b; if (c < a) { return 0xffffffff; } return c; } /* * We have to resolve the stream output state for empty geometry shaders. * In particular we've remapped the output indices when translating the * shaders so now the register_index variables in the stream output * state are incorrect and we need to remap them back to the correct * state. */ static void ResolveState(Device *pDevice) { if (pDevice->bound_empty_gs && pDevice->bound_vs && pDevice->bound_vs->state.tokens) { Shader *gs = pDevice->bound_empty_gs; Shader *vs = pDevice->bound_vs; boolean remapped = FALSE; struct pipe_context *pipe = pDevice->pipe; if (!gs->output_resolved) { for (unsigned i = 0; i < gs->state.stream_output.num_outputs; ++i) { unsigned mapping = ShaderFindOutputMapping(vs, gs->state.stream_output.output[i].register_index); if (mapping != gs->state.stream_output.output[i].register_index) { gs->state.stream_output.output[i].register_index = mapping; remapped = TRUE; } } if (remapped) { pipe->delete_gs_state(pipe, gs->handle); gs->handle = pipe->create_gs_state(pipe, &gs->state); } gs->output_resolved = TRUE; } pipe->bind_gs_state(pipe, gs->handle); } } static struct pipe_resource * create_null_index_buffer(struct pipe_context *ctx, uint num_indices, unsigned *restart_index, unsigned *index_size, unsigned *ib_offset) { unsigned buf_size = num_indices * sizeof(unsigned); unsigned *buf = (unsigned*)MALLOC(buf_size); struct pipe_resource *ibuf; memset(buf, 0, buf_size); ibuf = pipe_buffer_create_with_data(ctx, PIPE_BIND_INDEX_BUFFER, PIPE_USAGE_IMMUTABLE, buf_size, buf); *index_size = 4; *restart_index = 0xffffffff; *ib_offset = 0; FREE(buf); return ibuf; } /* * ---------------------------------------------------------------------- * * Draw -- * * The Draw function draws nonindexed primitives. * * ---------------------------------------------------------------------- */ void APIENTRY Draw(D3D10DDI_HDEVICE hDevice, // IN UINT VertexCount, // IN UINT StartVertexLocation) // IN { LOG_ENTRYPOINT(); Device *pDevice = CastDevice(hDevice); ResolveState(pDevice); assert(pDevice->primitive < PIPE_PRIM_MAX); util_draw_arrays(pDevice->pipe, pDevice->primitive, StartVertexLocation, VertexCount); } /* * ---------------------------------------------------------------------- * * DrawIndexed -- * * The DrawIndexed function draws indexed primitives. * * ---------------------------------------------------------------------- */ void APIENTRY DrawIndexed(D3D10DDI_HDEVICE hDevice, // IN UINT IndexCount, // IN UINT StartIndexLocation, // IN INT BaseVertexLocation) // IN { LOG_ENTRYPOINT(); Device *pDevice = CastDevice(hDevice); struct pipe_draw_info info; struct pipe_draw_start_count_bias draw; struct pipe_resource *null_ib = NULL; unsigned restart_index = pDevice->restart_index; unsigned index_size = pDevice->index_size; unsigned ib_offset = pDevice->ib_offset; assert(pDevice->primitive < PIPE_PRIM_MAX); /* XXX I don't think draw still needs this? */ if (!pDevice->index_buffer) { null_ib = create_null_index_buffer(pDevice->pipe, StartIndexLocation + IndexCount, &restart_index, &index_size, &ib_offset); } ResolveState(pDevice); util_draw_init_info(&info); info.index_size = index_size; info.mode = pDevice->primitive; draw.start = ClampedUAdd(StartIndexLocation, ib_offset / index_size); draw.count = IndexCount; info.index.resource = null_ib ? null_ib : pDevice->index_buffer; draw.index_bias = BaseVertexLocation; info.primitive_restart = TRUE; info.restart_index = restart_index; pDevice->pipe->draw_vbo(pDevice->pipe, &info, 0, NULL, &draw, 1); if (null_ib) { pipe_resource_reference(&null_ib, NULL); } } /* * ---------------------------------------------------------------------- * * DrawInstanced -- * * The DrawInstanced function draws particular instances * of nonindexed primitives. * * ---------------------------------------------------------------------- */ void APIENTRY DrawInstanced(D3D10DDI_HDEVICE hDevice, // IN UINT VertexCountPerInstance, // IN UINT InstanceCount, // IN UINT StartVertexLocation, // IN UINT StartInstanceLocation) // IN { LOG_ENTRYPOINT(); Device *pDevice = CastDevice(hDevice); if (!InstanceCount) { return; } ResolveState(pDevice); assert(pDevice->primitive < PIPE_PRIM_MAX); util_draw_arrays_instanced(pDevice->pipe, pDevice->primitive, StartVertexLocation, VertexCountPerInstance, StartInstanceLocation, InstanceCount); } /* * ---------------------------------------------------------------------- * * DrawIndexedInstanced -- * * The DrawIndexedInstanced function draws particular * instances of indexed primitives. * * ---------------------------------------------------------------------- */ void APIENTRY DrawIndexedInstanced(D3D10DDI_HDEVICE hDevice, // IN UINT IndexCountPerInstance, // IN UINT InstanceCount, // IN UINT StartIndexLocation, // IN INT BaseVertexLocation, // IN UINT StartInstanceLocation) // IN { LOG_ENTRYPOINT(); Device *pDevice = CastDevice(hDevice); struct pipe_draw_info info; struct pipe_draw_start_count_bias draw; struct pipe_resource *null_ib = NULL; unsigned restart_index = pDevice->restart_index; unsigned index_size = pDevice->index_size; unsigned ib_offset = pDevice->ib_offset; assert(pDevice->primitive < PIPE_PRIM_MAX); if (!InstanceCount) { return; } /* XXX I don't think draw still needs this? */ if (!pDevice->index_buffer) { null_ib = create_null_index_buffer(pDevice->pipe, StartIndexLocation + IndexCountPerInstance, &restart_index, &index_size, &ib_offset); } ResolveState(pDevice); util_draw_init_info(&info); info.index_size = index_size; info.mode = pDevice->primitive; draw.start = ClampedUAdd(StartIndexLocation, ib_offset / index_size); draw.count = IndexCountPerInstance; info.index.resource = null_ib ? null_ib : pDevice->index_buffer; draw.index_bias = BaseVertexLocation; info.start_instance = StartInstanceLocation; info.instance_count = InstanceCount; info.primitive_restart = TRUE; info.restart_index = restart_index; pDevice->pipe->draw_vbo(pDevice->pipe, &info, 0, NULL, &draw, 1); if (null_ib) { pipe_resource_reference(&null_ib, NULL); } } /* * ---------------------------------------------------------------------- * * DrawAuto -- * * The DrawAuto function works similarly to the Draw function, * except DrawAuto is used for the special case where vertex * data is written through the stream-output unit and then * recycled as a vertex buffer. The driver determines the number * of primitives, in part, by how much data was written to the * buffer through stream output. * * ---------------------------------------------------------------------- */ void APIENTRY DrawAuto(D3D10DDI_HDEVICE hDevice) // IN { LOG_ENTRYPOINT(); Device *pDevice = CastDevice(hDevice); struct pipe_draw_info info; struct pipe_draw_indirect_info indirect; if (!pDevice->draw_so_target) { LOG_UNSUPPORTED("DrawAuto without a set source buffer!"); return; } assert(pDevice->primitive < PIPE_PRIM_MAX); ResolveState(pDevice); util_draw_init_info(&info); info.mode = pDevice->primitive; memset(&indirect, 0, sizeof indirect); indirect.count_from_stream_output = pDevice->draw_so_target; pDevice->pipe->draw_vbo(pDevice->pipe, &info, 0, &indirect, NULL, 1); }