Created
March 14, 2025 19:56
-
-
Save unvestigate/65e819b2cca2c732a28bd912e11091a2 to your computer and use it in GitHub Desktop.
NanoVG renderer for the internal Basis RHI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "external/nanovg/nanovg_gfxapi.h" | |
| #include "external/nanovg/nanovg.h" | |
| #include <gfxapi/BasisRenderContext.h> | |
| #include <gfxapi/BasisRenderDevice.h> | |
| #include <gfxapi/BasisRenderTarget.h> | |
| #include <gfxapi/BasisViewport.h> | |
| #include <gfxapi/BasisBuffer.h> | |
| #include <gfxapi/BasisBufferDescription.h> | |
| #include <gfxapi/BasisTextureDescription.h> | |
| #include <gfxapi/BasisTexture.h> | |
| #include <gfxapi/BasisTextureSamplerDescription.h> | |
| #include <gfxapi/BasisTextureSampler.h> | |
| #include <gfxapi/BasisShaderFlags.h> | |
| #include <gfxapi/BasisShaderProgram.h> | |
| #include <gfxapi/BasisPipelineState.h> | |
| #include <gfxapi/BasisDynamicBindGroup.h> | |
| #include <resources/BasisResourceManager.h> | |
| #include <resources/BasisResourceListener.h> | |
| #include <resourcetypes/BasisShaderProgramResource.h> | |
| #include <renderer/BasisRenderer.h> | |
| #define NANOVG_SHADER_AA_PATH "game/shaders/nanovg/nanovg_aa.binshader" | |
| #define NANOVG_SHADER_NO_AA_PATH "game/shaders/nanovg/nanovg_no_aa.binshader" | |
| using namespace Basis; | |
| namespace | |
| { | |
| Mem::AllocatorHandle alloc(Mem::AllocatorRenderer); | |
| //#pragma pack(push) | |
| //#pragma pack(16) | |
| struct GfxApiNVGfragUniforms | |
| { | |
| float scissorMat[16]; | |
| float scissorExt[4]; | |
| float scissorScale[4]; | |
| float paintMat[16]; | |
| float extent[4]; | |
| float radius[4]; | |
| float feather[4]; | |
| float innerCol[4]; //NVGcolor innerCol; | |
| float outerCol[4]; //NVGcolor outerCol; | |
| float strokeMult[4]; | |
| int texType; | |
| int type; | |
| int padding1; | |
| int padding2; | |
| }; | |
| //#pragma pack(pop) | |
| struct VS_CONSTANTS | |
| { | |
| float dummy[16]; | |
| float viewSize[2]; | |
| }; | |
| enum GfxApiNVGshaderType | |
| { | |
| NSVG_SHADER_FILLGRAD = 0, | |
| NSVG_SHADER_FILLIMG, | |
| NSVG_SHADER_SIMPLE, | |
| NSVG_SHADER_IMG | |
| }; | |
| struct GfxApiNVGshader | |
| { | |
| GfxApi::ShaderProgram* shader; | |
| VS_CONSTANTS vc; | |
| }; | |
| struct GfxApiNVGtexture | |
| { | |
| int id; | |
| GfxApi::Texture* tex; | |
| int width, height; | |
| int type; | |
| int flags; | |
| }; | |
| enum GfxApiNVGcallType | |
| { | |
| GFXAPINVG_NONE = 0, | |
| GFXAPINVG_FILL, | |
| GFXAPINVG_CONVEXFILL, | |
| GFXAPINVG_STROKE, | |
| GFXAPINVG_TRIANGLES | |
| }; | |
| struct GfxApiNVGcall | |
| { | |
| int type; | |
| int image; | |
| int pathOffset; | |
| int pathCount; | |
| int triangleOffset; | |
| int triangleCount; | |
| int uniformOffset; | |
| }; | |
| struct GfxApiNVGpath | |
| { | |
| int fillOffset; | |
| int fillCount; | |
| int strokeOffset; | |
| int strokeCount; | |
| }; | |
| struct GfxApiNVGbuffer | |
| { | |
| unsigned int maxBufferEntries; | |
| unsigned int currentBufferEntry; | |
| GfxApi::Buffer* buffer; | |
| }; | |
| enum GfxApiNVGDepthStencilType | |
| { | |
| GfxApiNVGDepthStencilTypeDrawShapes = 0, | |
| GfxApiNVGDepthStencilTypeDrawAA, | |
| GfxApiNVGDepthStencilTypeFill, | |
| GfxApiNVGDepthStencilTypeDefault, | |
| GfxApiNVGDepthStencilTypeCount | |
| }; | |
| enum GfxApiNVGBlendType | |
| { | |
| GfxApiNVGBlendTypeBlend = 0, | |
| GfxApiNVGBlendTypeNoWrite, | |
| GfxApiNVGBlendTypeCount | |
| }; | |
| enum GfxApiNVGRasterizerType | |
| { | |
| GfxApiNVGRasterizerTypeNoCull = 0, | |
| GfxApiNVGRasterizerTypeCull, | |
| GfxApiNVGRasterizerTypeCount | |
| }; | |
| enum GfxApiNVGPrimitiveTopology | |
| { | |
| GfxApiNVGPrimitiveTopologyTriangleList = 0, | |
| GfxApiNVGPrimitiveTopologyTriangleStrip, | |
| GfxApiNVGPrimitiveTopologyCount | |
| }; | |
| struct GfxApiNVGpipelineState | |
| { | |
| GfxApi::PipelineState* pso; | |
| uint32_t key; | |
| }; | |
| const uint32_t PSO_COUNT = GfxApiNVGDepthStencilTypeCount * GfxApiNVGBlendTypeCount * GfxApiNVGRasterizerTypeCount * GfxApiNVGPrimitiveTopologyCount; | |
| struct GfxApiNVGcontext; | |
| class ShaderResourceListener : public Basis::ResourceListener | |
| { | |
| public: | |
| ShaderResourceListener(GfxApiNVGcontext* c) | |
| : ctxt(c) | |
| { | |
| } | |
| void resourceWasReloaded(Resource* resource); | |
| GfxApiNVGcontext* ctxt; | |
| }; | |
| struct GfxApiNVGcontext | |
| { | |
| Basis::GfxApi::RenderDevice* device; | |
| Basis::GfxApi::RenderContext* renderContext; | |
| ShaderResourceListener* shaderListener; | |
| ShaderProgramResource* shaderProgramResource = nullptr; | |
| GfxApi::UniformMetadataList* uniformMetadataList; | |
| GfxApiNVGshader shader; | |
| //bool needsExtraBufferBind; | |
| GfxApiNVGtexture* textures; | |
| GfxApi::ShaderBindingSlot textureBindingSlot; | |
| float view[2]; | |
| int ntextures; | |
| int ctextures; | |
| int textureId; | |
| GfxApi::TextureSampler* samplers[4]; | |
| GfxApi::ShaderBindingSlot samplerBindingSlot; | |
| int dummyTex; | |
| int fragSize; | |
| int flags; | |
| // Per frame buffers | |
| GfxApiNVGcall* calls; | |
| int ccalls; | |
| int ncalls; | |
| GfxApiNVGpath* paths; | |
| int cpaths; | |
| int npaths; | |
| NVGvertex* verts; | |
| int cverts; | |
| int nverts; | |
| unsigned char* uniforms; | |
| int cuniforms; | |
| int nuniforms; | |
| GfxApiNVGbuffer vertexBuffer; | |
| GfxApi::Buffer* fanIndexBuffer; | |
| GfxApi::Buffer* VSconstants; | |
| GfxApi::ShaderBindingSlot VSconstantBindingSlot; | |
| GfxApi::Buffer* PSconstants; | |
| GfxApi::ShaderBindingSlot PSconstantBindingSlot; | |
| GfxApi::DynamicBindGroup* bindGroup; | |
| GfxApi::TextureSampler* currentSampler; | |
| GfxApiNVGDepthStencilType currentDepthStencilType; | |
| GfxApiNVGBlendType currentBlendType; | |
| GfxApiNVGRasterizerType currentRasterizerType; | |
| GfxApiNVGPrimitiveTopology currentPrimTopoType; | |
| GfxApiNVGpipelineState pipelineStates[PSO_COUNT]; | |
| }; | |
| void initUniformMetadata(GfxApiNVGcontext* ctxt) | |
| { | |
| if (!ctxt->uniformMetadataList) | |
| { | |
| ctxt->uniformMetadataList = Mem::New<GfxApi::UniformMetadataList>(); | |
| } | |
| (*ctxt->uniformMetadataList) = ctxt->shader.shader->getUniformMetadata(); | |
| for (GfxApi::UniformMetadata& u : *ctxt->uniformMetadataList) | |
| { | |
| if (u.type == GfxApi::UniformTypeConstantBuffer) | |
| { | |
| u.hasDynamicOffset = true; | |
| } | |
| } | |
| } | |
| void ShaderResourceListener::resourceWasReloaded(Resource* resource) | |
| { | |
| // Release the old shader and get a reference to the updated one. | |
| // Then, release all the PSOs so that they get recreated on demand. | |
| BASIS_RELEASE_AND_NULL(ctxt->shader.shader); | |
| ctxt->shader.shader = ctxt->shaderProgramResource->getShaderProgram(); | |
| initUniformMetadata(ctxt); | |
| for (uint32_t i = 0; i < PSO_COUNT; ++i) | |
| { | |
| BASIS_RELEASE_AND_NULL(ctxt->pipelineStates[i].pso); | |
| ctxt->pipelineStates[i].key = 0xFFFFFFFF; | |
| } | |
| } | |
| int GfxApiNVG__maxi(int a, int b) | |
| { | |
| return a > b ? a : b; | |
| } | |
| GfxApiNVGtexture* GfxApiNVG__allocTexture(GfxApiNVGcontext* ctxt) | |
| { | |
| GfxApiNVGtexture* tex = nullptr; | |
| for (int i = 0; i < ctxt->ntextures; i++) | |
| { | |
| if (ctxt->textures[i].id == 0) | |
| { | |
| tex = &ctxt->textures[i]; | |
| break; | |
| } | |
| } | |
| if (tex == nullptr) | |
| { | |
| if (ctxt->ntextures + 1 > ctxt->ctextures) | |
| { | |
| GfxApiNVGtexture* textures; | |
| int ctextures = GfxApiNVG__maxi(ctxt->ntextures + 1, 4) + ctxt->ctextures / 2; // 1.5x Overallocate | |
| textures = (GfxApiNVGtexture*)realloc(ctxt->textures, sizeof(GfxApiNVGtexture) * ctextures); | |
| if (textures == NULL) return NULL; | |
| ctxt->textures = textures; | |
| ctxt->ctextures = ctextures; | |
| } | |
| tex = &ctxt->textures[ctxt->ntextures++]; | |
| } | |
| memset(tex, 0, sizeof(*tex)); | |
| tex->id = ++ctxt->textureId; | |
| return tex; | |
| } | |
| GfxApiNVGtexture* GfxApiNVG__findTexture(GfxApiNVGcontext* ctxt, int id) | |
| { | |
| int i; | |
| for (i = 0; i < ctxt->ntextures; i++) | |
| if (ctxt->textures[i].id == id) | |
| return &ctxt->textures[i]; | |
| return NULL; | |
| } | |
| int GfxApiNVG__deleteTexture(GfxApiNVGcontext* ctxt, int id) | |
| { | |
| int i; | |
| for (i = 0; i < ctxt->ntextures; i++) | |
| { | |
| if (ctxt->textures[i].id == id) | |
| { | |
| if (ctxt->textures[i].tex != 0) // && (ctxt->textures[i].flags & NVG_IMAGE_NODELETE) == 0) | |
| { | |
| BASIS_RELEASE_AND_NULL(ctxt->textures[i].tex); | |
| } | |
| memset(&ctxt->textures[i], 0, sizeof(ctxt->textures[i])); | |
| return 1; | |
| } | |
| } | |
| return 0; | |
| } | |
| void GfxApiNVG__copyMatrix3to4(float* pDest, const float* pSource) | |
| { | |
| unsigned int i; | |
| for (i = 0; i < 4; i++) | |
| { | |
| memcpy(&pDest[i * 4], &pSource[i * 3], sizeof(float) * 3); | |
| } | |
| } | |
| /*void GfxApiNVG__buildFanIndices(GfxApiNVGcontext* ctxt) | |
| { | |
| uint32_t index0 = 0; | |
| uint32_t index1 = 1; | |
| uint32_t index2 = 2; | |
| uint32_t current = 0; | |
| uint32_t* indices = ctxt->fanIndexBuffer->map<uint32_t>(Basis::GfxApi::BufferCPUAccessWriteOnly); | |
| while (current < (ctxt->vertexBuffer.maxBufferEntries - 3)) | |
| { | |
| indices[current++] = index0; | |
| indices[current++] = index1++; | |
| indices[current++] = index2++; | |
| } | |
| ctxt->fanIndexBuffer->unmap(); | |
| }*/ | |
| void GfxApiNVG__copyVerts(NVGvertex* pDest, const NVGvertex* pSource, unsigned int num) | |
| { | |
| unsigned int i; | |
| for (i = 0; i < num; i++) | |
| { | |
| pDest[i].x = pSource[i].x; | |
| pDest[i].y = pSource[i].y; | |
| pDest[i].u = pSource[i].u; | |
| pDest[i].v = pSource[i].v; | |
| } | |
| } | |
| unsigned int GfxApiNVG_updateVertexBuffer(GfxApiNVGcontext* ctxt, GfxApiNVGbuffer* buffer, const NVGvertex* verts, unsigned int nverts) | |
| { | |
| //unsigned int retEntry; | |
| //void* data = nullptr; | |
| if (nverts > buffer->maxBufferEntries) | |
| { | |
| BASIS_ASSERTF(false, "NanoVG vertex buffer too small. (%u > %u)", nverts, buffer->maxBufferEntries); | |
| return 0; | |
| } | |
| // Old, map-based version which uses BufferCPUAccessMapWriteNoOverwrite. | |
| /* | |
| if ((buffer->currentBufferEntry + nverts) >= buffer->maxBufferEntries) | |
| { | |
| buffer->currentBufferEntry = 0; | |
| data = buffer->buffer->map(Basis::GfxApi::BufferCPUAccessWriteOnly); | |
| } | |
| else | |
| { | |
| data = buffer->buffer->map(Basis::GfxApi::BufferCPUAccessMapWriteNoOverwrite); | |
| } | |
| GfxApiNVG__copyVerts((((NVGvertex*)data) + buffer->currentBufferEntry), (const NVGvertex*)verts, nverts); | |
| buffer->buffer->unmap(); | |
| retEntry = buffer->currentBufferEntry; | |
| buffer->currentBufferEntry += nverts; | |
| return retEntry; | |
| */ | |
| // New, writeToBuffer-based version, which overwrites the whole buffer every time and returns 0 (no offset). | |
| buffer->currentBufferEntry = 0; | |
| ctxt->renderContext->writeToBuffer(buffer->buffer, verts, nverts * sizeof(NVGvertex)); | |
| return 0; | |
| } | |
| void GfxApiNVG_setBuffers(GfxApiNVGcontext* ctxt, unsigned int dynamicOffset) | |
| { | |
| uint32_t stride = sizeof(NVGvertex); | |
| uint32_t offset = dynamicOffset * sizeof(NVGvertex); | |
| ctxt->renderContext->bindIndexBuffer(ctxt->fanIndexBuffer, 0); | |
| ctxt->renderContext->bindVertexBuffer(0, ctxt->vertexBuffer.buffer, stride, offset); | |
| } | |
| int GfxApiNVG__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); | |
| // Given a PSO key (constructed in GfxApiNVG__getPipelineState()) this function returns whether or not | |
| // the PSO exists. If it does not exist, index is set to point to the first free slot (ie. where the PSO can | |
| // be created). | |
| int GfxApiNVG__findPipelineStateIndex(GfxApiNVGcontext* ctxt, uint32_t key, uint32_t* index) | |
| { | |
| BASIS_ASSERT(key != 0xFFFFFFFF); | |
| BASIS_ASSERT(index != nullptr); | |
| for (uint32_t i = 0; i < PSO_COUNT; ++i) | |
| { | |
| if (ctxt->pipelineStates[i].key == key) | |
| { | |
| // We found the PSO we want. | |
| *index = i; | |
| return 1; | |
| } | |
| else if (ctxt->pipelineStates[i].key == 0xFFFFFFFF) | |
| { | |
| // We found a free position, the PSO was not found. | |
| *index = i; | |
| return 0; | |
| } | |
| } | |
| BASIS_ASSERTD(false, "Should not have come here."); | |
| return 0; | |
| } | |
| GfxApi::PipelineState* GfxApiNVG__createPipelineState(GfxApiNVGcontext* ctxt, uint8_t depthStencil, uint8_t blend, uint8_t rasterizer, uint8_t primitiveTopology) | |
| { | |
| BASIS_PRINTF("Creating NanoVG PSO, d/s: %u, blend: %u, raster: %u, prim: %u\n"); | |
| GfxApi::PipelineStateDescription desc; | |
| desc.primitiveTopology = (primitiveTopology == GfxApiNVGPrimitiveTopologyTriangleList) ? GfxApi::PrimitiveTopologyTriangleList : GfxApi::PrimitiveTopologyTriangleStrip; | |
| desc.depthStencil.depthTestEnabled = false; | |
| desc.depthStencil.depthWriteEnabled = false; | |
| desc.depthStencil.depthFunc = GfxApi::ComparisonFuncLessEqual; | |
| desc.depthStencil.stencilTestEnabled = true; | |
| desc.depthStencil.frontFace.readMask = 0xFF; | |
| desc.depthStencil.frontFace.writeMask = 0xFF; | |
| desc.depthStencil.backFace.readMask = 0xFF; | |
| desc.depthStencil.backFace.writeMask = 0xFF; | |
| if (depthStencil == GfxApiNVGDepthStencilTypeDrawShapes) | |
| { | |
| //const D3D11_DEPTH_STENCILOP_DESC frontOp = { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_INCR, D3D11_COMPARISON_ALWAYS }; | |
| //const D3D11_DEPTH_STENCILOP_DESC backOp = { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_DECR, D3D11_COMPARISON_ALWAYS }; | |
| desc.depthStencil.frontFace.stencilFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.frontFace.depthFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.frontFace.passOp = GfxApi::StencilOpIncrementWrap; | |
| desc.depthStencil.frontFace.stencilFunc = GfxApi::ComparisonFuncAlways; | |
| desc.depthStencil.backFace.stencilFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.backFace.depthFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.backFace.passOp = GfxApi::StencilOpDecrementWrap; | |
| desc.depthStencil.backFace.stencilFunc = GfxApi::ComparisonFuncAlways; | |
| } | |
| else if (depthStencil == GfxApiNVGDepthStencilTypeDrawAA) | |
| { | |
| //const D3D11_DEPTH_STENCILOP_DESC aaOp = { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_COMPARISON_EQUAL }; | |
| desc.depthStencil.frontFace.stencilFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.frontFace.depthFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.frontFace.passOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.frontFace.stencilFunc = GfxApi::ComparisonFuncEqual; | |
| desc.depthStencil.backFace.stencilFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.backFace.depthFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.backFace.passOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.backFace.stencilFunc = GfxApi::ComparisonFuncEqual; | |
| } | |
| else if (depthStencil == GfxApiNVGDepthStencilTypeFill) | |
| { | |
| //const D3D11_DEPTH_STENCILOP_DESC fillOp = { D3D11_STENCIL_OP_ZERO, D3D11_STENCIL_OP_ZERO, D3D11_STENCIL_OP_ZERO, D3D11_COMPARISON_NOT_EQUAL }; | |
| desc.depthStencil.frontFace.stencilFailOp = GfxApi::StencilOpZero; | |
| desc.depthStencil.frontFace.depthFailOp = GfxApi::StencilOpZero; | |
| desc.depthStencil.frontFace.passOp = GfxApi::StencilOpZero; | |
| desc.depthStencil.frontFace.stencilFunc = GfxApi::ComparisonFuncNotEqual; | |
| desc.depthStencil.backFace.stencilFailOp = GfxApi::StencilOpZero; | |
| desc.depthStencil.backFace.depthFailOp = GfxApi::StencilOpZero; | |
| desc.depthStencil.backFace.passOp = GfxApi::StencilOpZero; | |
| desc.depthStencil.backFace.stencilFunc = GfxApi::ComparisonFuncNotEqual; | |
| } | |
| else /*if (depthStencil == GfxApiNVGDepthStencilTypeDefault)*/ | |
| { | |
| //const D3D11_DEPTH_STENCILOP_DESC defaultOp = { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_COMPARISON_ALWAYS }; | |
| desc.depthStencil.frontFace.stencilFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.frontFace.depthFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.frontFace.passOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.frontFace.stencilFunc = GfxApi::ComparisonFuncAlways; | |
| desc.depthStencil.backFace.stencilFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.backFace.depthFailOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.backFace.passOp = GfxApi::StencilOpKeep; | |
| desc.depthStencil.backFace.stencilFunc = GfxApi::ComparisonFuncAlways; | |
| desc.depthStencil.stencilTestEnabled = false; | |
| } | |
| desc.rasterizer.fillMode = GfxApi::FillModeSolid; | |
| desc.rasterizer.cullMode = (rasterizer == GfxApiNVGRasterizerTypeNoCull) ? GfxApi::CullModeNone : GfxApi::CullModeBack; | |
| desc.rasterizer.frontCounterClockWise = true; | |
| desc.rasterizer.depthBias = 0; | |
| desc.rasterizer.depthBiasClamp = 0.0f; | |
| desc.rasterizer.slopeScaledDepthBias = 0.0f; | |
| desc.rasterizer.depthClipEnabled = true; | |
| desc.rasterizer.scissorTestEnabled = false; | |
| desc.rasterizer.antialiasedLinesEnabled = false; | |
| desc.blend.renderTargets.resize(1); | |
| desc.blend.renderTargets[0].blendEnabled = (blend == GfxApiNVGBlendTypeBlend); | |
| desc.blend.renderTargets[0].srcBlend = GfxApi::BlendOne; | |
| desc.blend.renderTargets[0].destBlend = GfxApi::BlendInvSrcAlpha; | |
| desc.blend.renderTargets[0].blendOp = GfxApi::BlendOpAdd; | |
| desc.blend.renderTargets[0].srcBlendAlpha = GfxApi::BlendOne; | |
| desc.blend.renderTargets[0].destBlendAlpha = GfxApi::BlendInvSrcAlpha; | |
| desc.blend.renderTargets[0].blendOpAlpha = GfxApi::BlendOpAdd; | |
| for (int i = 0; i < 4; ++i) | |
| desc.blend.renderTargets[0].writeMaskRGBA[i] = (blend == GfxApiNVGBlendTypeBlend); | |
| desc.shaderProgram = ctxt->shader.shader; | |
| desc.uniforms = ctxt->uniformMetadataList; | |
| return ctxt->device->createPipelineState(desc); | |
| } | |
| void GfxApiNVG__applyPipelineState(GfxApiNVGcontext* ctxt) | |
| { | |
| uint32_t key = (ctxt->currentDepthStencilType << 24) | (ctxt->currentBlendType << 16) | (ctxt->currentRasterizerType << 8) | ctxt->currentPrimTopoType; | |
| uint32_t index = 0; | |
| GfxApi::PipelineState* pso = nullptr; | |
| if (GfxApiNVG__findPipelineStateIndex(ctxt, key, &index)) | |
| { | |
| // The PSO was already created. | |
| pso = ctxt->pipelineStates[index].pso; | |
| } | |
| else | |
| { | |
| // The PSO was not yet created. Create and store for later use. | |
| pso = GfxApiNVG__createPipelineState(ctxt, ctxt->currentDepthStencilType, ctxt->currentBlendType, ctxt->currentRasterizerType, ctxt->currentPrimTopoType); | |
| ctxt->pipelineStates[index].pso = pso; | |
| ctxt->pipelineStates[index].key = key; | |
| } | |
| ctxt->renderContext->bindPipelineState(pso); | |
| } | |
| int GfxApiNVG__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); | |
| int GfxApiNVG__renderCreate(void* uptr) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| // TODO: Need to find a good value for this, and | |
| // Use the dynamic buffer fill technique to handle overflow | |
| ctxt->vertexBuffer.maxBufferEntries = 100000; | |
| ctxt->vertexBuffer.currentBufferEntry = 0; | |
| { | |
| // We don't specify any vertex stride here. Instead we specify | |
| // it when binding the VB to the pipeline. | |
| GfxApi::BufferDescription vbDesc; | |
| vbDesc.type = GfxApi::BufferTypeVertex; | |
| vbDesc.size = sizeof(NVGvertex) * ctxt->vertexBuffer.maxBufferEntries; | |
| vbDesc.cpuAccess = GfxApi::BufferCPUAccessWriteOnly; | |
| vbDesc.usage = GfxApi::BufferUsageDynamic; | |
| ctxt->vertexBuffer.buffer = ctxt->device->createBuffer(vbDesc); | |
| #ifndef BASIS_CONFIG_FINAL | |
| ctxt->vertexBuffer.buffer->setDebugName("NanoVG renderer vertex buffer"); | |
| #endif | |
| } | |
| { | |
| uint32_t index0 = 0; | |
| uint32_t index1 = 1; | |
| uint32_t index2 = 2; | |
| uint32_t current = 0; | |
| uint32_t* indices = Mem::NewArray<uint32_t>(ctxt->vertexBuffer.maxBufferEntries); | |
| while (current < (ctxt->vertexBuffer.maxBufferEntries - 3)) | |
| { | |
| indices[current++] = index0; | |
| indices[current++] = index1++; | |
| indices[current++] = index2++; | |
| } | |
| GfxApi::BufferDescription ibDesc; | |
| ibDesc.type = GfxApi::BufferTypeIndex; | |
| ibDesc.size = sizeof(uint32_t) * ctxt->vertexBuffer.maxBufferEntries; | |
| ibDesc.cpuAccess = GfxApi::BufferCPUAccessNone; | |
| ibDesc.usage = GfxApi::BufferUsageImmutable; | |
| ibDesc.indexBuffer.type = GfxApi::IndexBufferType32Bit; | |
| ibDesc.initialData = indices; | |
| ctxt->fanIndexBuffer = ctxt->device->createBuffer(ibDesc); | |
| #ifndef BASIS_CONFIG_FINAL | |
| ctxt->fanIndexBuffer->setDebugName("NanoVG renderer fan index buffer"); | |
| #endif | |
| Mem::DeleteArray(indices); | |
| } | |
| { | |
| const uint32_t MAX_UPDATES_PER_FRAME = 10; | |
| GfxApi::BufferDescription desc = {}; | |
| desc.type = GfxApi::BufferTypeConstant; | |
| desc.size = sizeof(VS_CONSTANTS); | |
| desc.cpuAccess = GfxApi::BufferCPUAccessWriteOnly; | |
| desc.usage = GfxApi::BufferUsageDynamic; | |
| desc.initialData = nullptr; | |
| desc.constantBuffer.dynamicThroughOffset = true; | |
| desc.constantBuffer.maxUpdateCount = MAX_UPDATES_PER_FRAME; | |
| ctxt->VSconstants = ctxt->device->createBuffer(desc); | |
| } | |
| { | |
| size_t size = sizeof(GfxApiNVGfragUniforms); | |
| if ((size % 16) != 0) | |
| { | |
| size += 16 - (size % 16); | |
| } | |
| ctxt->fragSize = (int)size; | |
| const uint32_t MAX_UPDATES_PER_FRAME = 250; | |
| GfxApi::BufferDescription desc = {}; | |
| desc.type = GfxApi::BufferTypeConstant; | |
| desc.size = size; | |
| desc.cpuAccess = GfxApi::BufferCPUAccessWriteOnly; | |
| desc.usage = GfxApi::BufferUsageDynamic; | |
| desc.initialData = nullptr; | |
| desc.constantBuffer.dynamicThroughOffset = true; | |
| desc.constantBuffer.maxUpdateCount = MAX_UPDATES_PER_FRAME; | |
| ctxt->PSconstants = ctxt->device->createBuffer(desc); | |
| } | |
| // Some platforms does not allow to have samples to unset textures. | |
| // Create empty one which is bound when there's no texture specified. | |
| ctxt->dummyTex = GfxApiNVG__renderCreateTexture(ctxt, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); | |
| { | |
| GfxApi::TextureSamplerDescription samplerDesc; | |
| samplerDesc.minFilter = GfxApi::TextureFilterLinear; | |
| samplerDesc.magFilter = GfxApi::TextureFilterLinear; | |
| samplerDesc.mipMapFilter = GfxApi::TextureFilterLinear; | |
| samplerDesc.addressModeW = GfxApi::TextureAddressModeWrap; | |
| samplerDesc.comparisonFunc = GfxApi::ComparisonFuncNever; | |
| samplerDesc.minLOD = 0.0f; | |
| samplerDesc.addressModeU = GfxApi::TextureAddressModeClamp; | |
| samplerDesc.addressModeV = GfxApi::TextureAddressModeClamp; | |
| ctxt->samplers[0] = ctxt->device->createTextureSampler(samplerDesc); | |
| samplerDesc.addressModeU = GfxApi::TextureAddressModeWrap; | |
| samplerDesc.addressModeV = GfxApi::TextureAddressModeClamp; | |
| ctxt->samplers[1] = ctxt->device->createTextureSampler(samplerDesc); | |
| samplerDesc.addressModeU = GfxApi::TextureAddressModeClamp; | |
| samplerDesc.addressModeV = GfxApi::TextureAddressModeWrap; | |
| ctxt->samplers[2] = ctxt->device->createTextureSampler(samplerDesc); | |
| samplerDesc.addressModeU = GfxApi::TextureAddressModeWrap; | |
| samplerDesc.addressModeV = GfxApi::TextureAddressModeWrap; | |
| ctxt->samplers[3] = ctxt->device->createTextureSampler(samplerDesc); | |
| } | |
| ctxt->bindGroup->init(ctxt->device, *ctxt->uniformMetadataList); | |
| ctxt->bindGroup->addBuffer(ctxt->VSconstantBindingSlot, ctxt->VSconstants, 0, ctxt->VSconstants->getBindSize()); | |
| ctxt->bindGroup->addBuffer(ctxt->PSconstantBindingSlot, ctxt->PSconstants, 0, ctxt->PSconstants->getBindSize()); | |
| return 1; // 1 == success. | |
| } | |
| int GfxApiNVG__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| GfxApiNVGtexture* tex = GfxApiNVG__allocTexture(ctxt); | |
| if (tex == NULL) | |
| { | |
| return 0; | |
| } | |
| if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) | |
| { | |
| BASIS_ASSERTD(false, "Generating NanoVG texture mip maps not yet implemented."); | |
| } | |
| tex->width = w; | |
| tex->height = h; | |
| tex->type = type; | |
| tex->flags = imageFlags; | |
| GfxApi::TextureDescription texDesc; | |
| texDesc.type = GfxApi::TextureType2D; | |
| texDesc.format = (type == NVG_TEXTURE_RGBA) ? GfxApi::TextureFormat_R8G8B8A8_UNORM : GfxApi::TextureFormat_R8_UNORM; | |
| texDesc.width = (uint32_t)w; | |
| texDesc.height = (uint32_t)h; | |
| texDesc.arrayLength = 1; | |
| texDesc.mipLevelCount = 1; | |
| texDesc.pixels = data; | |
| texDesc.cpuAccess = GfxApi::TextureCPUAccessWriteOnly; | |
| tex->tex = ctxt->device->createTexture(texDesc); | |
| return tex->id; | |
| } | |
| int GfxApiNVG__renderDeleteTexture(void* uptr, int image) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| return GfxApiNVG__deleteTexture(ctxt, image); | |
| } | |
| int GfxApiNVG__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| GfxApiNVGtexture* tex = GfxApiNVG__findTexture(ctxt, image); | |
| if (!tex) | |
| { | |
| return 0; | |
| } | |
| // The texture data passed in [data] is the whole texture, not only the rectangle to update. | |
| // Because of this we need to adjust the pointer to point to the start of the rectangle and | |
| // override the source row pitch. Otherwise the GfxApi::Texture will use the row pitch of the rect. | |
| uint32_t pixelWidthBytes = (tex->type == NVG_TEXTURE_RGBA) ? 4 : 1; | |
| uint32_t sourceRowPitch = tex->width * pixelWidthBytes; | |
| const uint8_t* rectData = (const uint8_t*)(data + (y * (tex->width * pixelWidthBytes)) + (x * pixelWidthBytes)); | |
| tex->tex->writeData(rectData, 0, 0, x, y, w, h, sourceRowPitch); | |
| return 1; | |
| } | |
| int GfxApiNVG__renderGetTextureSize(void* uptr, int image, int* w, int* h) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| GfxApiNVGtexture* tex = GfxApiNVG__findTexture(ctxt, image); | |
| if (!tex) | |
| { | |
| return 0; | |
| } | |
| *w = tex->width; | |
| *h = tex->height; | |
| return 1; | |
| } | |
| void GfxApiNVG__xformToMat3x3(float* m3, float* t) | |
| { | |
| m3[0] = t[0]; | |
| m3[1] = t[1]; | |
| m3[2] = 0.0f; | |
| m3[3] = t[2]; | |
| m3[4] = t[3]; | |
| m3[5] = 0.0f; | |
| m3[6] = t[4]; | |
| m3[7] = t[5]; | |
| m3[8] = 1.0f; | |
| } | |
| /*NVGcolor GfxApiNVG__premulColor(NVGcolor c) | |
| { | |
| c.r *= c.a; | |
| c.g *= c.a; | |
| c.b *= c.a; | |
| return c; | |
| }*/ | |
| void GfxApiNVG__premulColorIntoFloat4(NVGcolor c, float* f) | |
| { | |
| f[0] = c.r * c.a; | |
| f[2] = c.b * c.a; | |
| f[1] = c.g * c.a; | |
| f[3] = c.a; | |
| } | |
| int GfxApiNVG__convertPaint(GfxApiNVGcontext* ctxt, GfxApiNVGfragUniforms* frag, NVGpaint* paint, NVGscissor* scissor, | |
| float width, float fringe, float strokeThr) | |
| { | |
| GfxApiNVGtexture* tex = NULL; | |
| float invxform[6], paintMat[9], scissorMat[9]; | |
| memset(frag, 0, sizeof(*frag)); | |
| //frag->innerCol = GfxApiNVG__premulColor(paint->innerColor); | |
| //frag->outerCol = GfxApiNVG__premulColor(paint->outerColor); | |
| GfxApiNVG__premulColorIntoFloat4(paint->innerColor, frag->innerCol); | |
| GfxApiNVG__premulColorIntoFloat4(paint->outerColor, frag->outerCol); | |
| if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) | |
| { | |
| memset(scissorMat, 0, sizeof(scissorMat)); | |
| frag->scissorExt[0] = 1.0f; | |
| frag->scissorExt[1] = 1.0f; | |
| frag->scissorScale[0] = 1.0f; | |
| frag->scissorScale[1] = 1.0f; | |
| } | |
| else | |
| { | |
| nvgTransformInverse(invxform, scissor->xform); | |
| GfxApiNVG__xformToMat3x3(scissorMat, invxform); | |
| frag->scissorExt[0] = scissor->extent[0]; | |
| frag->scissorExt[1] = scissor->extent[1]; | |
| frag->scissorScale[0] = sqrtf(scissor->xform[0] * scissor->xform[0] + scissor->xform[2] * scissor->xform[2]) / fringe; | |
| frag->scissorScale[1] = sqrtf(scissor->xform[1] * scissor->xform[1] + scissor->xform[3] * scissor->xform[3]) / fringe; | |
| } | |
| GfxApiNVG__copyMatrix3to4(frag->scissorMat, scissorMat); | |
| frag->extent[0] = paint->extent[0]; | |
| frag->extent[1] = paint->extent[1]; | |
| frag->strokeMult[0] = (width * 0.5f + fringe * 0.5f) / fringe; | |
| frag->strokeMult[1] = strokeThr; | |
| if (paint->image != 0) | |
| { | |
| tex = GfxApiNVG__findTexture(ctxt, paint->image); | |
| if (tex == NULL) | |
| { | |
| return 0; | |
| } | |
| if ((tex->flags & NVG_IMAGE_FLIPY) != 0) | |
| { | |
| float m1[6], m2[6]; | |
| nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f); | |
| nvgTransformMultiply(m1, paint->xform); | |
| nvgTransformScale(m2, 1.0f, -1.0f); | |
| nvgTransformMultiply(m2, m1); | |
| nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f); | |
| nvgTransformMultiply(m1, m2); | |
| nvgTransformInverse(invxform, m1); | |
| } | |
| else | |
| { | |
| nvgTransformInverse(invxform, paint->xform); | |
| } | |
| frag->type = NSVG_SHADER_FILLIMG; | |
| #if NANOVG_GL_USE_UNIFORMBUFFER | |
| if (tex->type == NVG_TEXTURE_RGBA) | |
| { | |
| frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; | |
| } | |
| else | |
| { | |
| frag->texType = 2; | |
| } | |
| #else | |
| /*if (tex->type == NVG_TEXTURE_RGBA) | |
| frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f; | |
| else | |
| frag->texType = 2.0f;*/ | |
| // The original version above sets the values as floats. Why? | |
| if (tex->type == NVG_TEXTURE_RGBA) | |
| frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; | |
| else | |
| frag->texType = 2; | |
| #endif | |
| } | |
| else | |
| { | |
| frag->type = NSVG_SHADER_FILLGRAD; | |
| frag->radius[0] = paint->radius; | |
| frag->feather[0] = paint->feather; | |
| nvgTransformInverse(invxform, paint->xform); | |
| } | |
| GfxApiNVG__xformToMat3x3(paintMat, invxform); | |
| GfxApiNVG__copyMatrix3to4(frag->paintMat, paintMat); | |
| return 1; | |
| } | |
| GfxApiNVGfragUniforms* nvg__fragUniformPtr(GfxApiNVGcontext* ctxt, int i) | |
| { | |
| return (GfxApiNVGfragUniforms*)&ctxt->uniforms[i]; | |
| } | |
| void GfxApiNVG__setUniforms(GfxApiNVGcontext* ctxt, int uniformOffset, int image) | |
| { | |
| GfxApiNVGfragUniforms* frag = nvg__fragUniformPtr(ctxt, uniformOffset); | |
| GfxApiNVGtexture* tex = nullptr; | |
| ctxt->renderContext->updateDynamicBuffer(ctxt->PSconstants, frag, sizeof(GfxApiNVGfragUniforms)); | |
| if (image != 0) | |
| { | |
| tex = GfxApiNVG__findTexture(ctxt, image); | |
| } | |
| if (!tex) | |
| { | |
| tex = GfxApiNVG__findTexture(ctxt, ctxt->dummyTex); | |
| BASIS_ASSERT(tex != nullptr); // The dummy texture should always be available. | |
| } | |
| ctxt->bindGroup->begin(); | |
| ctxt->bindGroup->addTransientTextureSampler(ctxt->samplerBindingSlot, ctxt->currentSampler); | |
| ctxt->bindGroup->addTransientTexture(ctxt->textureBindingSlot, tex->tex); | |
| ctxt->renderContext->bindUniforms(ctxt->bindGroup->finalize()); | |
| } | |
| void GfxApiNVG__renderViewport(void* uptr, float width, float height, float devicePixelRatio) | |
| { | |
| NVG_NOTUSED(devicePixelRatio); | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| ctxt->shader.vc.viewSize[0] = width; | |
| ctxt->shader.vc.viewSize[1] = height; | |
| ctxt->renderContext->updateDynamicBuffer(ctxt->VSconstants, &ctxt->shader.vc, sizeof(VS_CONSTANTS)); | |
| } | |
| void GfxApiNVG__fill(GfxApiNVGcontext* ctxt, GfxApiNVGcall* call) | |
| { | |
| GfxApiNVGpath* paths = &ctxt->paths[call->pathOffset]; | |
| int i, npaths = call->pathCount; | |
| // Draw shapes | |
| ctxt->currentDepthStencilType = GfxApiNVGDepthStencilTypeDrawShapes; //D3D_API_2(D3D->pDeviceContext, OMSetDepthStencilState, D3D->pDepthStencilDrawShapes, 0); | |
| ctxt->currentBlendType = GfxApiNVGBlendTypeNoWrite; //D3D_API_3(D3D->pDeviceContext, OMSetBlendState, D3D->pBSNoWrite, NULL, 0xFFFFFFFF); | |
| ctxt->currentRasterizerType = GfxApiNVGRasterizerTypeNoCull; //D3D_API_1(D3D->pDeviceContext, RSSetState, D3D->pRSNoCull); | |
| // set bindpoint for solid loc | |
| GfxApiNVG__setUniforms(ctxt, call->uniformOffset, 0); | |
| ctxt->currentPrimTopoType = GfxApiNVGPrimitiveTopologyTriangleList; //D3D_API_1(D3D->pDeviceContext, IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); | |
| GfxApiNVG__applyPipelineState(ctxt); | |
| for (i = 0; i < npaths; i++) | |
| { | |
| if (paths[i].fillCount > 0) | |
| { | |
| unsigned int numIndices = ((paths[i].fillCount - 2) * 3); | |
| BASIS_ASSERT(numIndices < ctxt->vertexBuffer.maxBufferEntries); | |
| if (numIndices < ctxt->vertexBuffer.maxBufferEntries) | |
| { | |
| ctxt->renderContext->drawIndexed(numIndices, 0, paths[i].fillOffset); //D3D_API_3(D3D->pDeviceContext, DrawIndexed, numIndices, 0, paths[i].fillOffset); | |
| } | |
| } | |
| } | |
| // Draw anti-aliased pixels | |
| ctxt->currentRasterizerType = GfxApiNVGRasterizerTypeCull; //D3D_API_1(D3D->pDeviceContext, RSSetState, D3D->pRSCull); | |
| ctxt->currentPrimTopoType = GfxApiNVGPrimitiveTopologyTriangleStrip; //D3D_API_1(D3D->pDeviceContext, IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); | |
| ctxt->currentBlendType = GfxApiNVGBlendTypeBlend; //D3D_API_3(D3D->pDeviceContext, OMSetBlendState, D3D->pBSBlend, NULL, 0xFFFFFFFF); | |
| GfxApiNVG__setUniforms(ctxt, call->uniformOffset + ctxt->fragSize, call->image); | |
| if (ctxt->flags & NVG_ANTIALIAS) | |
| { | |
| ctxt->currentDepthStencilType = GfxApiNVGDepthStencilTypeDrawAA; //D3D_API_2(D3D->pDeviceContext, OMSetDepthStencilState, D3D->pDepthStencilDrawAA, 0); | |
| GfxApiNVG__applyPipelineState(ctxt); | |
| // Draw fringes | |
| for (i = 0; i < npaths; i++) | |
| { | |
| ctxt->renderContext->draw(paths[i].strokeCount, paths[i].strokeOffset); //D3D_API_2(D3D->pDeviceContext, Draw, paths[i].strokeCount, paths[i].strokeOffset); | |
| } | |
| } | |
| // Draw fill | |
| ctxt->currentRasterizerType = GfxApiNVGRasterizerTypeNoCull; //D3D_API_1(D3D->pDeviceContext, RSSetState, D3D->pRSNoCull); | |
| ctxt->currentDepthStencilType = GfxApiNVGDepthStencilTypeFill; //D3D_API_2(D3D->pDeviceContext, OMSetDepthStencilState, D3D->pDepthStencilFill, 0); | |
| GfxApiNVG__applyPipelineState(ctxt); | |
| ctxt->renderContext->draw(call->triangleCount, call->triangleOffset); //D3D_API_2(D3D->pDeviceContext, Draw, call->triangleCount, call->triangleOffset); | |
| ctxt->currentDepthStencilType = GfxApiNVGDepthStencilTypeDefault; //D3D_API_2(D3D->pDeviceContext, OMSetDepthStencilState, D3D->pDepthStencilDefault, 0); | |
| } | |
| void GfxApiNVG__convexFill(GfxApiNVGcontext* ctxt, GfxApiNVGcall* call) | |
| { | |
| GfxApiNVGpath* paths = &ctxt->paths[call->pathOffset]; | |
| int i, npaths = call->pathCount; | |
| GfxApiNVG__setUniforms(ctxt, call->uniformOffset, call->image); | |
| ctxt->currentPrimTopoType = GfxApiNVGPrimitiveTopologyTriangleList; //D3D_API_1(D3D->pDeviceContext, IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); | |
| GfxApiNVG__applyPipelineState(ctxt); | |
| for (i = 0; i < npaths; i++) | |
| { | |
| // Draws a fan using indices to fake it up, since there isn't a fan primitive in D3D11. | |
| if (paths[i].fillCount > 2) | |
| { | |
| unsigned int numIndices = ((paths[i].fillCount - 2) * 3); | |
| BASIS_ASSERT(numIndices < ctxt->vertexBuffer.maxBufferEntries); | |
| if (numIndices < ctxt->vertexBuffer.maxBufferEntries) | |
| { | |
| ctxt->renderContext->drawIndexed(numIndices, 0, paths[i].fillOffset); //D3D_API_3(D3D->pDeviceContext, DrawIndexed, numIndices, 0, paths[i].fillOffset); | |
| } | |
| } | |
| } | |
| // Draw fringes | |
| ctxt->currentPrimTopoType = GfxApiNVGPrimitiveTopologyTriangleStrip; //D3D_API_1(D3D->pDeviceContext, IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); | |
| GfxApiNVG__applyPipelineState(ctxt); | |
| for (i = 0; i < npaths; i++) | |
| { | |
| if (paths[i].strokeCount > 0) | |
| { | |
| ctxt->renderContext->draw(paths[i].strokeCount, paths[i].strokeOffset); //D3D_API_2(D3D->pDeviceContext, Draw, paths[i].strokeCount, paths[i].strokeOffset); | |
| } | |
| } | |
| } | |
| void GfxApiNVG__stroke(GfxApiNVGcontext* ctxt, GfxApiNVGcall* call) | |
| { | |
| GfxApiNVGpath* paths = &ctxt->paths[call->pathOffset]; | |
| int npaths = call->pathCount, i; | |
| ctxt->currentPrimTopoType = GfxApiNVGPrimitiveTopologyTriangleStrip; //D3D_API_1(D3D->pDeviceContext, IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); | |
| if (ctxt->flags & NVG_STENCIL_STROKES) | |
| { | |
| // Fill the stroke base without overlap | |
| ctxt->currentDepthStencilType = GfxApiNVGDepthStencilTypeDefault; //D3D_API_2(D3D->pDeviceContext, OMSetDepthStencilState, D3D->pDepthStencilDefault, 0); | |
| GfxApiNVG__setUniforms(ctxt, call->uniformOffset + ctxt->fragSize, call->image); | |
| GfxApiNVG__applyPipelineState(ctxt); | |
| for (i = 0; i < npaths; i++) | |
| { | |
| ctxt->renderContext->draw(paths[i].strokeCount, paths[i].strokeOffset); //D3D_API_2(D3D->pDeviceContext, Draw, paths[i].strokeCount, paths[i].strokeOffset); | |
| } | |
| // Draw anti-aliased pixels. | |
| GfxApiNVG__setUniforms(ctxt, call->uniformOffset, call->image); | |
| ctxt->currentDepthStencilType = GfxApiNVGDepthStencilTypeDrawAA; //D3D_API_2(D3D->pDeviceContext, OMSetDepthStencilState, D3D->pDepthStencilDrawAA, 0); | |
| GfxApiNVG__applyPipelineState(ctxt); | |
| for (i = 0; i < npaths; i++) | |
| { | |
| ctxt->renderContext->draw(paths[i].strokeCount, paths[i].strokeOffset); //D3D_API_2(D3D->pDeviceContext, Draw, paths[i].strokeCount, paths[i].strokeOffset); | |
| } | |
| // Clear stencil buffer. | |
| ctxt->currentDepthStencilType = GfxApiNVGDepthStencilTypeFill; //D3D_API_2(D3D->pDeviceContext, OMSetDepthStencilState, D3D->pDepthStencilFill, 0); | |
| GfxApiNVG__applyPipelineState(ctxt); | |
| for (i = 0; i < npaths; i++) | |
| { | |
| ctxt->renderContext->draw(paths[i].strokeCount, paths[i].strokeOffset); //D3D_API_2(D3D->pDeviceContext, Draw, paths[i].strokeCount, paths[i].strokeOffset); | |
| } | |
| ctxt->currentDepthStencilType = GfxApiNVGDepthStencilTypeDefault; //D3D_API_2(D3D->pDeviceContext, OMSetDepthStencilState, D3D->pDepthStencilDefault, 0); | |
| } | |
| else | |
| { | |
| GfxApiNVG__setUniforms(ctxt, call->uniformOffset, call->image); | |
| GfxApiNVG__applyPipelineState(ctxt); | |
| // Draw Strokes | |
| for (i = 0; i < npaths; i++) | |
| { | |
| ctxt->renderContext->draw(paths[i].strokeCount, paths[i].strokeOffset); //D3D_API_2(D3D->pDeviceContext, Draw, paths[i].strokeCount, paths[i].strokeOffset); | |
| } | |
| } | |
| } | |
| void GfxApiNVG__triangles(GfxApiNVGcontext* ctxt, GfxApiNVGcall* call) | |
| { | |
| ctxt->currentPrimTopoType = GfxApiNVGPrimitiveTopologyTriangleList; //D3D_API_1(D3D->pDeviceContext, IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); | |
| GfxApiNVG__setUniforms(ctxt, call->uniformOffset, call->image); | |
| GfxApiNVG__applyPipelineState(ctxt); | |
| ctxt->renderContext->draw(call->triangleCount, call->triangleOffset); //D3D_API_2(D3D->pDeviceContext, Draw, call->triangleCount, call->triangleOffset); | |
| } | |
| void GfxApiNVG__renderCancel(void* uptr) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| ctxt->nverts = 0; | |
| ctxt->npaths = 0; | |
| ctxt->ncalls = 0; | |
| ctxt->nuniforms = 0; | |
| } | |
| void GfxApiNVG__renderFlush(void* uptr) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| if (ctxt->ncalls > 0) | |
| { | |
| unsigned int buffer0Offset = GfxApiNVG_updateVertexBuffer(ctxt, &ctxt->vertexBuffer, ctxt->verts, ctxt->nverts); | |
| GfxApiNVG_setBuffers(ctxt, buffer0Offset); | |
| // Draw shapes | |
| ctxt->currentDepthStencilType = GfxApiNVGDepthStencilTypeDefault; //D3D_API_2(D3D->pDeviceContext, OMSetDepthStencilState, D3D->pDepthStencilDefault, 0); | |
| ctxt->currentBlendType = GfxApiNVGBlendTypeBlend; //D3D_API_3(D3D->pDeviceContext, OMSetBlendState, D3D->pBSBlend, NULL, 0xFFFFFFFF); | |
| ctxt->currentRasterizerType = GfxApiNVGRasterizerTypeCull; //D3D_API_1(D3D->pDeviceContext, RSSetState, D3D->pRSCull); | |
| for (int i = 0; i < ctxt->ncalls; i++) | |
| { | |
| GfxApiNVGcall* call = &ctxt->calls[i]; | |
| if (call->image != 0) | |
| { | |
| GfxApiNVGtexture* tex = GfxApiNVG__findTexture(ctxt, call->image); | |
| if (tex) | |
| { | |
| ctxt->currentSampler = ctxt->samplers[(tex->flags & NVG_IMAGE_REPEATX ? 1 : 0) + (tex->flags & NVG_IMAGE_REPEATY ? 2 : 0)]; | |
| } | |
| else | |
| { | |
| ctxt->currentSampler = ctxt->samplers[0]; | |
| } | |
| } | |
| else | |
| { | |
| // Some APIs (eg. Metal) does not like unbound samplers. Bind any of the samplers here even if it is not used. | |
| ctxt->currentSampler = ctxt->samplers[0]; | |
| } | |
| if (call->type == GFXAPINVG_FILL) | |
| GfxApiNVG__fill(ctxt, call); | |
| else if (call->type == GFXAPINVG_CONVEXFILL) | |
| GfxApiNVG__convexFill(ctxt, call); | |
| else if (call->type == GFXAPINVG_STROKE) | |
| GfxApiNVG__stroke(ctxt, call); | |
| else if (call->type == GFXAPINVG_TRIANGLES) | |
| GfxApiNVG__triangles(ctxt, call); | |
| } | |
| } | |
| // Reset calls | |
| ctxt->nverts = 0; | |
| ctxt->npaths = 0; | |
| ctxt->ncalls = 0; | |
| ctxt->nuniforms = 0; | |
| } | |
| int GfxApiNVG__maxVertCount(const NVGpath* paths, int npaths) | |
| { | |
| int i, count = 0; | |
| for (i = 0; i < npaths; i++) { | |
| count += paths[i].nfill; | |
| count += paths[i].nstroke; | |
| } | |
| return count; | |
| } | |
| GfxApiNVGcall* GfxApiNVG__allocCall(GfxApiNVGcontext* ctxt) | |
| { | |
| GfxApiNVGcall* ret = NULL; | |
| if (ctxt->ncalls + 1 > ctxt->ccalls) | |
| { | |
| GfxApiNVGcall* calls = NULL; | |
| int ccalls = GfxApiNVG__maxi(ctxt->ncalls + 1, 128) + ctxt->ccalls / 2; // 1.5x Overallocate | |
| calls = (GfxApiNVGcall*)realloc(ctxt->calls, sizeof(GfxApiNVGcall) * ccalls); | |
| if (calls == NULL) return NULL; | |
| ctxt->calls = calls; | |
| ctxt->ccalls = ccalls; | |
| } | |
| ret = &ctxt->calls[ctxt->ncalls++]; | |
| memset(ret, 0, sizeof(GfxApiNVGcall)); | |
| return ret; | |
| } | |
| int GfxApiNVG__allocPaths(GfxApiNVGcontext* ctxt, int n) | |
| { | |
| int ret = 0; | |
| if (ctxt->npaths + n > ctxt->cpaths) | |
| { | |
| GfxApiNVGpath* paths = NULL; | |
| int cpaths = GfxApiNVG__maxi(ctxt->npaths + n, 128) + ctxt->cpaths / 2; // 1.5x Overallocate | |
| paths = (GfxApiNVGpath*)realloc(ctxt->paths, sizeof(GfxApiNVGpath) * cpaths); | |
| if (paths == NULL) return -1; | |
| ctxt->paths = paths; | |
| ctxt->cpaths = cpaths; | |
| } | |
| ret = ctxt->npaths; | |
| ctxt->npaths += n; | |
| return ret; | |
| } | |
| int GfxApiNVG__allocVerts(GfxApiNVGcontext* ctxt, int n) | |
| { | |
| int ret = 0; | |
| if (ctxt->nverts + n > ctxt->cverts) | |
| { | |
| NVGvertex* verts = NULL; | |
| int cverts = GfxApiNVG__maxi(ctxt->nverts + n, 4096) + ctxt->cverts / 2; // 1.5x Overallocate | |
| verts = (NVGvertex*)realloc(ctxt->verts, sizeof(NVGvertex) * cverts); | |
| if (verts == NULL) return -1; | |
| ctxt->verts = verts; | |
| ctxt->cverts = cverts; | |
| } | |
| ret = ctxt->nverts; | |
| ctxt->nverts += n; | |
| return ret; | |
| } | |
| int GfxApiNVG__allocFragUniforms(GfxApiNVGcontext* ctxt, int n) | |
| { | |
| int ret = 0, structSize = ctxt->fragSize; | |
| if (ctxt->nuniforms + n > ctxt->cuniforms) | |
| { | |
| unsigned char* uniforms = NULL; | |
| int cuniforms = GfxApiNVG__maxi(ctxt->nuniforms + n, 128) + ctxt->cuniforms / 2; // 1.5x Overallocate | |
| uniforms = (unsigned char*)realloc(ctxt->uniforms, structSize * cuniforms); | |
| if (uniforms == NULL) return -1; | |
| ctxt->uniforms = uniforms; | |
| ctxt->cuniforms = cuniforms; | |
| } | |
| ret = ctxt->nuniforms * structSize; | |
| ctxt->nuniforms += n; | |
| return ret; | |
| } | |
| void GfxApiNVG__vset(struct NVGvertex* vtx, float x, float y, float u, float v) | |
| { | |
| vtx->x = x; | |
| vtx->y = y; | |
| vtx->u = u; | |
| vtx->v = v; | |
| } | |
| void GfxApiNVG__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, | |
| NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| GfxApiNVGcall* call = GfxApiNVG__allocCall(ctxt); | |
| NVGvertex* quad; | |
| GfxApiNVGfragUniforms* frag = NULL; | |
| int i, maxverts, offset; | |
| if (call == NULL) return; | |
| call->type = GFXAPINVG_FILL; | |
| call->triangleCount = 4; | |
| call->pathOffset = GfxApiNVG__allocPaths(ctxt, npaths); | |
| if (call->pathOffset == -1) goto error; | |
| call->pathCount = npaths; | |
| call->image = paint->image; | |
| NVG_NOTUSED(compositeOperation); | |
| if (npaths == 1 && paths[0].convex) | |
| { | |
| call->type = GFXAPINVG_CONVEXFILL; | |
| call->triangleCount = 0; // Bounding box fill quad not needed for convex fill | |
| } | |
| // Allocate vertices for all the paths. | |
| maxverts = GfxApiNVG__maxVertCount(paths, npaths) + call->triangleCount; | |
| offset = GfxApiNVG__allocVerts(ctxt, maxverts); | |
| if (offset == -1) goto error; | |
| for (i = 0; i < npaths; i++) | |
| { | |
| GfxApiNVGpath* copy = &ctxt->paths[call->pathOffset + i]; | |
| const NVGpath* path = &paths[i]; | |
| memset(copy, 0, sizeof(GfxApiNVGpath)); | |
| if (path->nfill > 0) | |
| { | |
| copy->fillOffset = offset; | |
| copy->fillCount = path->nfill; | |
| memcpy(&ctxt->verts[offset], path->fill, sizeof(struct NVGvertex) * path->nfill); | |
| offset += path->nfill; | |
| } | |
| if (path->nstroke > 0) | |
| { | |
| copy->strokeOffset = offset; | |
| copy->strokeCount = path->nstroke; | |
| memcpy(&ctxt->verts[offset], path->stroke, sizeof(struct NVGvertex) * path->nstroke); | |
| offset += path->nstroke; | |
| } | |
| } | |
| if (call->type == GFXAPINVG_FILL) | |
| { | |
| // Quad | |
| call->triangleOffset = offset; | |
| quad = &ctxt->verts[call->triangleOffset]; | |
| GfxApiNVG__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f); | |
| GfxApiNVG__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f); | |
| GfxApiNVG__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f); | |
| GfxApiNVG__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f); | |
| call->uniformOffset = GfxApiNVG__allocFragUniforms(ctxt, 2); | |
| if (call->uniformOffset == -1) goto error; | |
| // Simple shader for stencil | |
| frag = nvg__fragUniformPtr(ctxt, call->uniformOffset); | |
| memset(frag, 0, sizeof(*frag)); | |
| frag->strokeMult[1] = -1.0f; | |
| frag->type = NSVG_SHADER_SIMPLE; | |
| // Fill shader | |
| GfxApiNVG__convertPaint(ctxt, nvg__fragUniformPtr(ctxt, call->uniformOffset + ctxt->fragSize), paint, scissor, fringe, fringe, -1.0f); | |
| } | |
| else | |
| { | |
| call->uniformOffset = GfxApiNVG__allocFragUniforms(ctxt, 1); | |
| if (call->uniformOffset == -1) goto error; | |
| // Fill shader | |
| GfxApiNVG__convertPaint(ctxt, nvg__fragUniformPtr(ctxt, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f); | |
| } | |
| return; | |
| error: | |
| // We get here if call alloc was ok, but something else is not. | |
| // Roll back the last call to prevent drawing it. | |
| if (ctxt->ncalls > 0) ctxt->ncalls--; | |
| } | |
| void GfxApiNVG__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, | |
| NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| GfxApiNVGcall* call = GfxApiNVG__allocCall(ctxt); | |
| int i, maxverts, offset; | |
| if (call == NULL) return; | |
| call->type = GFXAPINVG_STROKE; | |
| call->pathOffset = GfxApiNVG__allocPaths(ctxt, npaths); | |
| if (call->pathOffset == -1) goto error; | |
| call->pathCount = npaths; | |
| call->image = paint->image; | |
| NVG_NOTUSED(compositeOperation); | |
| // Allocate vertices for all the paths. | |
| maxverts = GfxApiNVG__maxVertCount(paths, npaths); | |
| offset = GfxApiNVG__allocVerts(ctxt, maxverts); | |
| if (offset == -1) goto error; | |
| for (i = 0; i < npaths; i++) | |
| { | |
| GfxApiNVGpath* copy = &ctxt->paths[call->pathOffset + i]; | |
| const NVGpath* path = &paths[i]; | |
| memset(copy, 0, sizeof(GfxApiNVGpath)); | |
| if (path->nstroke) | |
| { | |
| copy->strokeOffset = offset; | |
| copy->strokeCount = path->nstroke; | |
| memcpy(&ctxt->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); | |
| offset += path->nstroke; | |
| } | |
| } | |
| if (ctxt->flags & NVG_STENCIL_STROKES) | |
| { | |
| // Fill shader | |
| call->uniformOffset = GfxApiNVG__allocFragUniforms(ctxt, 2); | |
| if (call->uniformOffset == -1) goto error; | |
| GfxApiNVG__convertPaint(ctxt, nvg__fragUniformPtr(ctxt, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); | |
| GfxApiNVG__convertPaint(ctxt, nvg__fragUniformPtr(ctxt, call->uniformOffset + ctxt->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f / 255.0f); | |
| } | |
| else | |
| { | |
| // Fill shader | |
| call->uniformOffset = GfxApiNVG__allocFragUniforms(ctxt, 1); | |
| if (call->uniformOffset == -1) goto error; | |
| GfxApiNVG__convertPaint(ctxt, nvg__fragUniformPtr(ctxt, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); | |
| } | |
| return; | |
| error: | |
| // We get here if call alloc was ok, but something else is not. | |
| // Roll back the last call to prevent drawing it. | |
| if (ctxt->ncalls > 0) ctxt->ncalls--; | |
| } | |
| void GfxApiNVG__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, | |
| NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| GfxApiNVGcall* call = GfxApiNVG__allocCall(ctxt); | |
| GfxApiNVGfragUniforms* frag = NULL; | |
| if (call == NULL) return; | |
| call->type = GFXAPINVG_TRIANGLES; | |
| call->image = paint->image; | |
| NVG_NOTUSED(compositeOperation); | |
| // Allocate vertices for all the paths. | |
| call->triangleOffset = GfxApiNVG__allocVerts(ctxt, nverts); | |
| if (call->triangleOffset == -1) goto error; | |
| call->triangleCount = nverts; | |
| memcpy(&ctxt->verts[call->triangleOffset], verts, sizeof(NVGvertex) * nverts); | |
| // Fill shader | |
| call->uniformOffset = GfxApiNVG__allocFragUniforms(ctxt, 1); | |
| if (call->uniformOffset == -1) goto error; | |
| frag = nvg__fragUniformPtr(ctxt, call->uniformOffset); | |
| GfxApiNVG__convertPaint(ctxt, frag, paint, scissor, 1.0f, fringe, -1.0f); | |
| frag->type = NSVG_SHADER_IMG; | |
| return; | |
| error: | |
| // We get here if call alloc was ok, but something else is not. | |
| // Roll back the last call to prevent drawing it. | |
| if (ctxt->ncalls > 0) ctxt->ncalls--; | |
| } | |
| void GfxApiNVG__renderDelete(void* uptr) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)uptr; | |
| if (ctxt->uniformMetadataList) | |
| { | |
| Mem::Delete(ctxt->uniformMetadataList); | |
| } | |
| Mem::Delete(ctxt->bindGroup); | |
| BASIS_RELEASE_AND_NULL(ctxt->shader.shader); | |
| ctxt->shaderProgramResource->removeListener(ctxt->shaderListener); | |
| ctxt->shaderProgramResource->release(); | |
| Mem::Delete(ctxt->shaderListener); | |
| for (int i = 0; i < ctxt->ntextures; i++) | |
| { | |
| if (ctxt->textures[i].tex != 0 /*&& (ctxt->textures[i].flags & NVG_IMAGE_NODELETE) == 0*/) | |
| { | |
| BASIS_RELEASE_AND_NULL(ctxt->textures[i].tex); | |
| } | |
| } | |
| for (int i = 0; i < 4; i++) | |
| { | |
| BASIS_RELEASE_AND_NULL(ctxt->samplers[i]); | |
| } | |
| BASIS_RELEASE_AND_NULL(ctxt->vertexBuffer.buffer); | |
| BASIS_RELEASE_AND_NULL(ctxt->VSconstants); | |
| BASIS_RELEASE_AND_NULL(ctxt->PSconstants); | |
| BASIS_RELEASE_AND_NULL(ctxt->fanIndexBuffer); | |
| for (uint32_t i = 0; i < PSO_COUNT; ++i) | |
| { | |
| BASIS_RELEASE_AND_NULL(ctxt->pipelineStates[i].pso); | |
| } | |
| free(ctxt->textures); | |
| free(ctxt->paths); | |
| free(ctxt->verts); | |
| free(ctxt->uniforms); | |
| free(ctxt->calls); | |
| alloc.free(ctxt); | |
| } | |
| } | |
| struct NVGcontext* nvgCreateGfxApi(Basis::ResourceManager* resMgr, Basis::GfxApi::RenderDevice* device, int flags) | |
| { | |
| NVGparams params; | |
| NVGcontext* ctx = nullptr; | |
| GfxApiNVGcontext* gfxApiCtxt = (GfxApiNVGcontext*)alloc.allocate(sizeof(GfxApiNVGcontext)); | |
| if (!gfxApiCtxt) | |
| { | |
| goto error; | |
| } | |
| memset(gfxApiCtxt, 0, sizeof(GfxApiNVGcontext)); | |
| for (uint32_t i = 0; i < PSO_COUNT; ++i) | |
| { | |
| // Set the keys to 0xFFFFFFFF to mark them as "free". | |
| gfxApiCtxt->pipelineStates[i].key = 0xFFFFFFFF; | |
| } | |
| gfxApiCtxt->bindGroup = Mem::New<GfxApi::DynamicBindGroup>(); | |
| gfxApiCtxt->shaderProgramResource = resMgr->acquire<ShaderProgramResource>( | |
| (flags & NVG_ANTIALIAS) ? NANOVG_SHADER_AA_PATH : NANOVG_SHADER_NO_AA_PATH); | |
| gfxApiCtxt->shaderListener = Mem::New<ShaderResourceListener>(gfxApiCtxt); | |
| gfxApiCtxt->shaderProgramResource->addListener(gfxApiCtxt->shaderListener); | |
| // Needs to be scoped because of goto. | |
| //{ | |
| // // Currently all backends except D3D11 needs the extra buffer map() call. | |
| // // See GfxApiNVG__setUniforms() for more info. | |
| // Renderer* renderer = Renderer::getPtr(); | |
| // GfxApi::GfxApiBackendType backendType = renderer->getBackendType(); | |
| // gfxApiCtxt->needsExtraBufferBind = (backendType != GfxApi::GfxApiBackendTypeD3D11); | |
| //} | |
| gfxApiCtxt->shader.shader = gfxApiCtxt->shaderProgramResource->getShaderProgram(); | |
| initUniformMetadata(gfxApiCtxt); | |
| gfxApiCtxt->VSconstantBindingSlot = gfxApiCtxt->shaderProgramResource->getDescription().getUniformBindingSlot("VS_CONSTANTS"); | |
| gfxApiCtxt->textureBindingSlot = gfxApiCtxt->shaderProgramResource->getDescription().getUniformBindingSlot("g_texture"); | |
| gfxApiCtxt->samplerBindingSlot = gfxApiCtxt->shaderProgramResource->getDescription().getUniformBindingSlot("g_sampler"); | |
| gfxApiCtxt->PSconstantBindingSlot = gfxApiCtxt->shaderProgramResource->getDescription().getUniformBindingSlot("PS_CONSTANTS"); | |
| gfxApiCtxt->device = device; | |
| gfxApiCtxt->renderContext = device->getImmediateContext(); | |
| gfxApiCtxt->flags = flags; | |
| memset(¶ms, 0, sizeof(params)); | |
| params.renderCreate = GfxApiNVG__renderCreate; | |
| params.renderCreateTexture = GfxApiNVG__renderCreateTexture; | |
| params.renderDeleteTexture = GfxApiNVG__renderDeleteTexture; | |
| params.renderUpdateTexture = GfxApiNVG__renderUpdateTexture; | |
| params.renderGetTextureSize = GfxApiNVG__renderGetTextureSize; | |
| params.renderViewport = GfxApiNVG__renderViewport; | |
| params.renderCancel = GfxApiNVG__renderCancel; | |
| params.renderFlush = GfxApiNVG__renderFlush; | |
| params.renderFill = GfxApiNVG__renderFill; | |
| params.renderStroke = GfxApiNVG__renderStroke; | |
| params.renderTriangles = GfxApiNVG__renderTriangles; | |
| params.renderDelete = GfxApiNVG__renderDelete; | |
| params.userPtr = gfxApiCtxt; | |
| params.edgeAntiAlias = flags & NVG_ANTIALIAS ? 1 : 0; | |
| ctx = nvgCreateInternal(¶ms); | |
| if (!ctx) goto error; | |
| return ctx; | |
| error: | |
| // 'gfxApiCtxt' is freed by nvgDeleteInternal. | |
| if (ctx) nvgDeleteInternal(ctx); | |
| return NULL; | |
| } | |
| void nvgDeleteGfxApi(struct NVGcontext* ctx) | |
| { | |
| nvgDeleteInternal(ctx); | |
| } | |
| void nvGfxApiBeginFrame(NVGcontext* ctx) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)nvgInternalParams(ctx)->userPtr; | |
| ctxt->VSconstants->resetDynamicOffset(); | |
| ctxt->PSconstants->resetDynamicOffset(); | |
| } | |
| int nvGfxApiCreateImageFromTexture(NVGcontext* ctx, Basis::GfxApi::Texture* texture, int imageFlags) | |
| { | |
| GfxApiNVGcontext* ctxt = (GfxApiNVGcontext*)nvgInternalParams(ctx)->userPtr; | |
| GfxApiNVGtexture* tex = GfxApiNVG__allocTexture(ctxt); | |
| if (tex == NULL) | |
| { | |
| return 0; | |
| } | |
| tex->width = texture->getDescription().width; | |
| tex->height = texture->getDescription().height; | |
| tex->type = NVG_TEXTURE_RGBA; | |
| tex->flags = imageFlags; | |
| tex->tex = texture; | |
| tex->tex->addReference(); | |
| return tex->id; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment