/* ** Polygon Doom software renderer ** Copyright (c) 2016 Magnus Norddahl ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. ** */ #pragma once #include "swrenderer/drawers/r_draw.h" #include "swrenderer/drawers/r_thread.h" #include "polyrenderer/drawers/screen_triangle.h" #include "polyrenderer/math/gpu_types.h" #include "polyrenderer/drawers/poly_draw_args.h" #include "polyrenderer/drawers/poly_vertex_shader.h" class DCanvas; class PolyDrawerCommand; class PolyInputAssembly; class PolyDepthStencil; struct PolyPushConstants; class PolyTriangleDrawer { public: static void SetViewport(const DrawerCommandQueuePtr &queue, int x, int y, int width, int height, DCanvas *canvas, PolyDepthStencil *depthStencil); static void SetInputAssembly(const DrawerCommandQueuePtr &queue, PolyInputAssembly *input); static void SetVertexBuffer(const DrawerCommandQueuePtr &queue, const void *vertices); static void SetIndexBuffer(const DrawerCommandQueuePtr &queue, const void *elements); static void SetViewpointUniforms(const DrawerCommandQueuePtr &queue, const HWViewpointUniforms *uniforms); static void SetDepthClamp(const DrawerCommandQueuePtr &queue, bool on); static void SetDepthMask(const DrawerCommandQueuePtr &queue, bool on); static void SetDepthFunc(const DrawerCommandQueuePtr &queue, int func); static void SetDepthRange(const DrawerCommandQueuePtr &queue, float min, float max); static void SetDepthBias(const DrawerCommandQueuePtr &queue, float depthBiasConstantFactor, float depthBiasSlopeFactor); static void SetColorMask(const DrawerCommandQueuePtr &queue, bool r, bool g, bool b, bool a); static void SetStencil(const DrawerCommandQueuePtr &queue, int stencilRef, int op); static void SetCulling(const DrawerCommandQueuePtr &queue, int mode); static void EnableClipDistance(const DrawerCommandQueuePtr &queue, int num, bool state); static void EnableStencil(const DrawerCommandQueuePtr &queue, bool on); static void SetScissor(const DrawerCommandQueuePtr &queue, int x, int y, int w, int h); static void EnableDepthTest(const DrawerCommandQueuePtr &queue, bool on); static void SetRenderStyle(const DrawerCommandQueuePtr &queue, FRenderStyle style); static void SetTexture(const DrawerCommandQueuePtr &queue, void *pixels, int width, int height); static void SetShader(const DrawerCommandQueuePtr &queue, int specialEffect, int effectState, bool alphaTest); static void PushStreamData(const DrawerCommandQueuePtr &queue, const StreamData &data, const PolyPushConstants &constants); static void PushMatrices(const DrawerCommandQueuePtr &queue, const VSMatrix &modelMatrix, const VSMatrix &normalModelMatrix, const VSMatrix &textureMatrix); static void ClearDepth(const DrawerCommandQueuePtr &queue, float value); static void ClearStencil(const DrawerCommandQueuePtr &queue, uint8_t value); static void Draw(const DrawerCommandQueuePtr &queue, int index, int vcount, PolyDrawMode mode = PolyDrawMode::Triangles); static void DrawIndexed(const DrawerCommandQueuePtr &queue, int index, int count, PolyDrawMode mode = PolyDrawMode::Triangles); static bool IsBgra(); // Old softpoly/swrenderer interface static void SetCullCCW(const DrawerCommandQueuePtr &queue, bool ccw); static void SetTwoSided(const DrawerCommandQueuePtr &queue, bool twosided); static void SetWeaponScene(const DrawerCommandQueuePtr &queue, bool enable); static void SetModelVertexShader(const DrawerCommandQueuePtr &queue, int frame1, int frame2, float interpolationFactor); static void SetTransform(const DrawerCommandQueuePtr &queue, const Mat4f *objectToClip, const Mat4f *objectToWorld); static void PushDrawArgs(const DrawerCommandQueuePtr &queue, const PolyDrawArgs &args); }; class PolyDepthStencil { public: PolyDepthStencil(int width, int height) : width(width), height(height), depthbuffer(width * height), stencilbuffer(width * height) { } int Width() const { return width; } int Height() const { return height; } float *DepthValues() { return depthbuffer.data(); } uint8_t *StencilValues() { return stencilbuffer.data(); } private: int width; int height; std::vector depthbuffer; std::vector stencilbuffer; }; struct PolyPushConstants { int uTextureMode; float uAlphaThreshold; Vec2f uClipSplit; // Lighting + Fog float uLightLevel; float uFogDensity; float uLightFactor; float uLightDist; int uFogEnabled; // dynamic lights int uLightIndex; }; class PolyInputAssembly { public: virtual void Load(PolyTriangleThreadData *thread, const void *vertices, int index) = 0; }; class PolySWInputAssembly : public PolyInputAssembly { public: void Load(PolyTriangleThreadData *thread, const void *vertices, int index) override; }; class PolyTriangleThreadData { public: PolyTriangleThreadData(int32_t core, int32_t num_cores, int32_t numa_node, int32_t num_numa_nodes, int numa_start_y, int numa_end_y) : core(core), num_cores(num_cores), numa_node(numa_node), num_numa_nodes(num_numa_nodes), numa_start_y(numa_start_y), numa_end_y(numa_end_y) { swVertexShader.drawargs = &drawargs; } void ClearDepth(float value); void ClearStencil(uint8_t value); void SetViewport(int x, int y, int width, int height, uint8_t *dest, int dest_width, int dest_height, int dest_pitch, bool dest_bgra, PolyDepthStencil *depthstencil); void SetTransform(const Mat4f *objectToClip, const Mat4f *objectToWorld); void SetCullCCW(bool value) { ccw = value; } void SetTwoSided(bool value) { twosided = value; } void SetWeaponScene(bool value) { depthbias = value ? -1.0f : 0.0f; } void SetModelVertexShader(int frame1, int frame2, float interpolationFactor) { modelFrame1 = frame1; modelFrame2 = frame2; swVertexShader.modelInterpolationFactor = interpolationFactor; } void SetInputAssembly(PolyInputAssembly *input) { inputAssembly = input; } void SetVertexBuffer(const void *data) { vertices = data; } void SetIndexBuffer(const void *data) { elements = (const unsigned int *)data; } void SetViewpointUniforms(const HWViewpointUniforms *uniforms); void SetDepthClamp(bool on); void SetDepthMask(bool on); void SetDepthFunc(int func); void SetDepthRange(float min, float max); void SetDepthBias(float depthBiasConstantFactor, float depthBiasSlopeFactor); void SetColorMask(bool r, bool g, bool b, bool a); void SetStencil(int stencilRef, int op); void SetCulling(int mode); void EnableClipDistance(int num, bool state); void EnableStencil(bool on); void SetScissor(int x, int y, int w, int h); void EnableDepthTest(bool on); void SetRenderStyle(FRenderStyle style); void SetTexture(void *pixels, int width, int height); void SetShader(int specialEffect, int effectState, bool alphaTest); void UpdateClip(); void PushDrawArgs(const PolyDrawArgs &args); void PushStreamData(const StreamData &data, const PolyPushConstants &constants); void PushMatrices(const VSMatrix &modelMatrix, const VSMatrix &normalModelMatrix, const VSMatrix &textureMatrix); void DrawIndexed(int index, int count, PolyDrawMode mode); void Draw(int index, int vcount, PolyDrawMode mode); int32_t core; int32_t num_cores; int32_t numa_node; int32_t num_numa_nodes; int numa_start_y; int numa_end_y; bool line_skipped_by_thread(int line) { return line < numa_start_y || line >= numa_end_y || line % num_cores != core; } int skipped_by_thread(int first_line) { int clip_first_line = MAX(first_line, numa_start_y); int core_skip = (num_cores - (clip_first_line - core) % num_cores) % num_cores; return clip_first_line + core_skip - first_line; } int count_for_thread(int first_line, int count) { count = MIN(count, numa_end_y - first_line); int c = (count - skipped_by_thread(first_line) + num_cores - 1) / num_cores; return MAX(c, 0); } // Varyings float worldposX[MAXWIDTH]; float worldposY[MAXWIDTH]; float worldposZ[MAXWIDTH]; uint32_t texel[MAXWIDTH]; int32_t texelV[MAXWIDTH]; uint16_t lightarray[MAXWIDTH]; uint32_t dynlights[MAXWIDTH]; float depthvalues[MAXWIDTH]; uint8_t alphatestbuffer[MAXWIDTH]; static PolyTriangleThreadData *Get(DrawerThread *thread); int dest_pitch = 0; int dest_width = 0; int dest_height = 0; bool dest_bgra = false; uint8_t *dest = nullptr; PolyDepthStencil *depthstencil = nullptr; float depthbias = 0.0f; int viewport_y = 0; struct ClipRect { int left = 0; int top = 0; int right = 0; int bottom = 0; } clip, scissor; FRenderStyle RenderStyle; int SpecialEffect = EFF_NONE; int EffectState = 0; bool AlphaTest = false; PolyDrawArgs drawargs; const void *vertices = nullptr; const unsigned int *elements = nullptr; PolyMainVertexShader mainVertexShader; int modelFrame1 = -1; int modelFrame2 = -1; PolySWVertexShader swVertexShader; private: ShadedTriVertex ShadeVertex(int index); void DrawShadedPoint(const ShadedTriVertex *const* vertex); void DrawShadedLine(const ShadedTriVertex *const* vertices); void DrawShadedTriangle(const ShadedTriVertex *const* vertices, bool ccw, TriDrawTriangleArgs *args); static bool IsDegenerate(const ShadedTriVertex *const* vertices); static bool IsFrontfacing(TriDrawTriangleArgs *args); int ClipEdge(const ShadedTriVertex *const* verts); int viewport_x = 0; int viewport_width = 0; int viewport_height = 0; bool ccw = true; bool twosided = true; PolyInputAssembly *inputAssembly = nullptr; enum { max_additional_vertices = 16 }; float weightsbuffer[max_additional_vertices * 3 * 2]; float *weights = nullptr; }; class PolyDrawerCommand : public DrawerCommand { public: }; class PolySetDepthClampCommand : public PolyDrawerCommand { public: PolySetDepthClampCommand(bool on) : on(on) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetDepthClamp(on); } private: bool on; }; class PolySetDepthMaskCommand : public PolyDrawerCommand { public: PolySetDepthMaskCommand(bool on) : on(on) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetDepthMask(on); } private: bool on; }; class PolySetDepthFuncCommand : public PolyDrawerCommand { public: PolySetDepthFuncCommand(int func) : func(func) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetDepthFunc(func); } private: int func; }; class PolySetDepthRangeCommand : public PolyDrawerCommand { public: PolySetDepthRangeCommand(float min, float max) : min(min), max(max) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetDepthRange(min, max); } private: float min; float max; }; class PolySetDepthBiasCommand : public PolyDrawerCommand { public: PolySetDepthBiasCommand(float depthBiasConstantFactor, float depthBiasSlopeFactor) : depthBiasConstantFactor(depthBiasConstantFactor), depthBiasSlopeFactor(depthBiasSlopeFactor) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetDepthBias(depthBiasConstantFactor, depthBiasSlopeFactor); } private: float depthBiasConstantFactor; float depthBiasSlopeFactor; }; class PolySetColorMaskCommand : public PolyDrawerCommand { public: PolySetColorMaskCommand(bool r, bool g, bool b, bool a) : r(r), g(g), b(b), a(a) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetColorMask(r, g, b, a); } private: bool r; bool g; bool b; bool a; }; class PolySetStencilCommand : public PolyDrawerCommand { public: PolySetStencilCommand(int stencilRef, int op) : stencilRef(stencilRef), op(op) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetStencil(stencilRef, op); } private: int stencilRef; int op; }; class PolySetCullingCommand : public PolyDrawerCommand { public: PolySetCullingCommand(int mode) : mode(mode) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetCulling(mode); } private: int mode; }; class PolyEnableClipDistanceCommand : public PolyDrawerCommand { public: PolyEnableClipDistanceCommand(int num, bool state) : num(num), state(state) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->EnableClipDistance(num, state); } private: int num; bool state; }; class PolyEnableStencilCommand : public PolyDrawerCommand { public: PolyEnableStencilCommand(bool on) : on(on) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->EnableStencil(on); } private: bool on; }; class PolySetScissorCommand : public PolyDrawerCommand { public: PolySetScissorCommand(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetScissor(x, y, w, h); } private: int x; int y; int w; int h; }; class PolyEnableDepthTestCommand : public PolyDrawerCommand { public: PolyEnableDepthTestCommand(bool on) : on(on) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->EnableDepthTest(on); } private: bool on; }; class PolySetRenderStyleCommand : public PolyDrawerCommand { public: PolySetRenderStyleCommand(FRenderStyle style) : style(style) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetRenderStyle(style); } private: FRenderStyle style; }; class PolySetTextureCommand : public PolyDrawerCommand { public: PolySetTextureCommand(void *pixels, int width, int height) : pixels(pixels), width(width), height(height) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetTexture(pixels, width, height); } private: void *pixels; int width; int height; }; class PolySetShaderCommand : public PolyDrawerCommand { public: PolySetShaderCommand(int specialEffect, int effectState, bool alphaTest) : specialEffect(specialEffect), effectState(effectState), alphaTest(alphaTest) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetShader(specialEffect, effectState, alphaTest); } private: int specialEffect; int effectState; bool alphaTest; }; class PolySetVertexBufferCommand : public PolyDrawerCommand { public: PolySetVertexBufferCommand(const void *vertices) : vertices(vertices) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetVertexBuffer(vertices); } private: const void *vertices; }; class PolySetIndexBufferCommand : public PolyDrawerCommand { public: PolySetIndexBufferCommand(const void *indices) : indices(indices) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetIndexBuffer(indices); } private: const void *indices; }; class PolySetInputAssemblyCommand : public PolyDrawerCommand { public: PolySetInputAssemblyCommand(PolyInputAssembly *input) : input(input) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetInputAssembly(input); } private: PolyInputAssembly *input; }; class PolySetTransformCommand : public PolyDrawerCommand { public: PolySetTransformCommand(const Mat4f *objectToClip, const Mat4f *objectToWorld) : objectToClip(objectToClip), objectToWorld(objectToWorld) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetTransform(objectToClip, objectToWorld); } private: const Mat4f *objectToClip; const Mat4f *objectToWorld; }; class PolySetCullCCWCommand : public PolyDrawerCommand { public: PolySetCullCCWCommand(bool ccw) : ccw(ccw) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetCullCCW(ccw); } private: bool ccw; }; class PolySetTwoSidedCommand : public PolyDrawerCommand { public: PolySetTwoSidedCommand(bool twosided) : twosided(twosided) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetTwoSided(twosided); } private: bool twosided; }; class PolySetWeaponSceneCommand : public PolyDrawerCommand { public: PolySetWeaponSceneCommand(bool value) : value(value) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetWeaponScene(value); } private: bool value; }; class PolySetModelVertexShaderCommand : public PolyDrawerCommand { public: PolySetModelVertexShaderCommand(int frame1, int frame2, float interpolationFactor) : frame1(frame1), frame2(frame2), interpolationFactor(interpolationFactor) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetModelVertexShader(frame1, frame2, interpolationFactor); } private: int frame1; int frame2; float interpolationFactor; }; class PolyClearDepthCommand : public PolyDrawerCommand { public: PolyClearDepthCommand(float value) : value(value) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->ClearDepth(value); } private: float value; }; class PolyClearStencilCommand : public PolyDrawerCommand { public: PolyClearStencilCommand(uint8_t value) : value(value) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->ClearStencil(value); } private: uint8_t value; }; class PolySetViewportCommand : public PolyDrawerCommand { public: PolySetViewportCommand(int x, int y, int width, int height, uint8_t *dest, int dest_width, int dest_height, int dest_pitch, bool dest_bgra, PolyDepthStencil *depthstencil) : x(x), y(y), width(width), height(height), dest(dest), dest_width(dest_width), dest_height(dest_height), dest_pitch(dest_pitch), dest_bgra(dest_bgra), depthstencil(depthstencil) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetViewport(x, y, width, height, dest, dest_width, dest_height, dest_pitch, dest_bgra, depthstencil); } private: int x; int y; int width; int height; uint8_t *dest; int dest_width; int dest_height; int dest_pitch; bool dest_bgra; PolyDepthStencil *depthstencil; }; class PolySetViewpointUniformsCommand : public PolyDrawerCommand { public: PolySetViewpointUniformsCommand(const HWViewpointUniforms *uniforms) : uniforms(uniforms) {} void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetViewpointUniforms(uniforms); } private: const HWViewpointUniforms *uniforms; }; class PolyPushMatricesCommand : public PolyDrawerCommand { public: PolyPushMatricesCommand(const VSMatrix &modelMatrix, const VSMatrix &normalModelMatrix, const VSMatrix &textureMatrix) : modelMatrix(modelMatrix), normalModelMatrix(normalModelMatrix), textureMatrix(textureMatrix) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->PushMatrices(modelMatrix, normalModelMatrix, textureMatrix); } private: VSMatrix modelMatrix; VSMatrix normalModelMatrix; VSMatrix textureMatrix; }; class PolyPushStreamDataCommand : public PolyDrawerCommand { public: PolyPushStreamDataCommand(const StreamData &data, const PolyPushConstants &constants) : data(data), constants(constants) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->PushStreamData(data, constants); } private: StreamData data; PolyPushConstants constants; }; class PolyPushDrawArgsCommand : public PolyDrawerCommand { public: PolyPushDrawArgsCommand(const PolyDrawArgs &args) : args(args) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->PushDrawArgs(args); } private: PolyDrawArgs args; }; class PolyDrawCommand : public PolyDrawerCommand { public: PolyDrawCommand(int index, int count, PolyDrawMode mode) : index(index), count(count), mode(mode) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->Draw(index, count, mode); } private: int index; int count; PolyDrawMode mode; }; class PolyDrawIndexedCommand : public PolyDrawerCommand { public: PolyDrawIndexedCommand(int index, int count, PolyDrawMode mode) : index(index), count(count), mode(mode) { } void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->DrawIndexed(index, count, mode); } private: int index; int count; PolyDrawMode mode; };