#include "thin3d/thin3d.h" #include namespace Draw { #if 0 // A problem is that we can't get the D3Dcompiler.dll without using a later SDK than 7.1, which was the last that // supported XP. A possible solution might be here: // https://tedwvc.wordpress.com/2014/01/01/how-to-target-xp-with-vc2012-or-vc2013-and-continue-to-use-the-windows-8-x-sdk/ class D3D11Pipeline; class D3D11BlendState; class D3D11DepthStencilState; class D3D11SamplerState; class D3D11RasterState; class D3D11DrawContext : public DrawContext { public: D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext); ~D3D11DrawContext(); const DeviceCaps &GetDeviceCaps() const override { return caps_; } uint32_t GetSupportedShaderLanguages() const override { return (uint32_t)ShaderLanguage::HLSL_D3D11 | (uint32_t)ShaderLanguage::HLSL_D3D11_BYTECODE; } uint32_t GetDataFormatSupport(DataFormat fmt) const override; InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override; DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override; BlendState *CreateBlendState(const BlendStateDesc &desc) override; SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override; RasterState *CreateRasterState(const RasterStateDesc &desc) override; Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override; Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override; Texture *CreateTexture(const TextureDesc &desc) override; ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize) override; Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override; void fbo_copy_image(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; int fbo_preferred_z_bitdepth() override; // These functions should be self explanatory. void BindFramebufferAsRenderTarget(Framebuffer *fbo) override; // color must be 0, for now. void fbo_bind_as_texture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override; void fbo_bind_for_read(Framebuffer *fbo) override; void fbo_bind_backbuffer_as_render_target() override; uintptr_t fbo_get_api_texture(Framebuffer *fbo, int channelBit, int attachment) override; void fbo_get_dimensions(Framebuffer *fbo, int *w, int *h) override; void BindTextures(int start, int count, Texture **textures) override; void BindSamplerStates(int start, int count, SamplerState **states) override; void BindPipeline(Pipeline *pipeline) override; void BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) override; void BindIndexBuffer(Buffer *indexBuffer, int offset) override; // Raster state void SetScissorRect(int left, int top, int width, int height) override; void SetViewports(int count, Viewport *viewports) override; void SetBlendFactor(float color[4]) override { memcpy(blendFactor_, color, sizeof(float) * 4); } void Draw(int vertexCount, int offset) override; void DrawIndexed(int vertexCount, int offset) override; void DrawUP(const void *vdata, int vertexCount) override; void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal); std::string GetInfoString(InfoField info) const override { switch (info) { case APIVERSION: return "DirectX 11.0"; case VENDORSTRING: return "N/A"; case VENDOR: return "-"; case RENDERER: return "N/A"; case SHADELANGVERSION: return "N/A"; case APINAME: return "Direct3D 11"; default: return "?"; } } uintptr_t GetNativeObject(NativeObject obj) const override { switch (obj) { case NativeObject::DEVICE: return (uintptr_t)device_; case NativeObject::CONTEXT: return (uintptr_t)context_; default: return 0; } } void HandleEvent(Event ev) override {} private: void ApplyCurrentState(); ID3D11Device *device_; ID3D11DeviceContext *context_; D3D11Pipeline *curPipeline_; DeviceCaps caps_; D3D11BlendState *curBlend_ = nullptr; D3D11DepthStencilState *curDepth_ = nullptr; D3D11RasterState *curRaster_ = nullptr; ID3D11InputLayout *curInputLayout_ = nullptr; // Dynamic state float blendFactor_[4]; bool blendFactorDirty_ = false; uint8_t stencilRef_; bool stencilRefDirty_; }; D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *context) : device_(device), context_(context) { } D3D11DrawContext::~D3D11DrawContext() { } void D3D11DrawContext::SetViewports(int count, Viewport *viewports) { // Intentionally binary compatible context_->RSSetViewports(count, (D3D11_VIEWPORT *)viewports); } void D3D11DrawContext::SetScissorRect(int left, int top, int width, int height) { D3D11_RECT rc; rc.left = left; rc.top = top; rc.right = left + width; rc.bottom = top + height; context_->RSSetScissorRects(1, &rc); } class D3D11DepthStencilState : public DepthStencilState { public: ~D3D11DepthStencilState() { dss->Release(); } ID3D11DepthStencilState *dss; }; static const D3D11_COMPARISON_FUNC compareToD3D11[] = { D3D11_COMPARISON_NEVER, D3D11_COMPARISON_LESS, D3D11_COMPARISON_EQUAL, D3D11_COMPARISON_LESS_EQUAL, D3D11_COMPARISON_GREATER, D3D11_COMPARISON_NOT_EQUAL, D3D11_COMPARISON_GREATER_EQUAL, D3D11_COMPARISON_ALWAYS }; static const D3D11_STENCIL_OP stencilOpToD3D11[] = { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_ZERO, D3D11_STENCIL_OP_REPLACE, D3D11_STENCIL_OP_INCR_SAT, D3D11_STENCIL_OP_DECR_SAT, D3D11_STENCIL_OP_INVERT, D3D11_STENCIL_OP_INCR, D3D11_STENCIL_OP_DECR, }; DXGI_FORMAT dataFormatToD3D11(DataFormat format) { switch (format) { case DataFormat::R32_FLOAT: return DXGI_FORMAT_R32_FLOAT; case DataFormat::R32G32_FLOAT: return DXGI_FORMAT_R32G32_FLOAT; case DataFormat::R32G32B32_FLOAT: return DXGI_FORMAT_R32G32B32_FLOAT; case DataFormat::R32G32B32A32_FLOAT: return DXGI_FORMAT_R32G32B32A32_FLOAT; case DataFormat::R8G8B8A8_UNORM: return DXGI_FORMAT_R8G8B8A8_UNORM; case DataFormat::ETC1: default: return DXGI_FORMAT_UNKNOWN; } } inline void CopyStencilSide(D3D11_DEPTH_STENCILOP_DESC &side, const StencilSide &input) { side.StencilFunc = compareToD3D11[(int)input.compareOp]; side.StencilDepthFailOp = stencilOpToD3D11[(int)input.depthFailOp]; side.StencilFailOp = stencilOpToD3D11[(int)input.failOp]; side.StencilPassOp = stencilOpToD3D11[(int)input.passOp]; } DepthStencilState *D3D11DrawContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) { D3D11DepthStencilState *ds = new D3D11DepthStencilState(); D3D11_DEPTH_STENCIL_DESC d3ddesc{}; d3ddesc.DepthEnable = desc.depthTestEnabled; d3ddesc.DepthWriteMask = desc.depthWriteEnabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; d3ddesc.DepthFunc = compareToD3D11[(int)desc.depthCompare]; d3ddesc.StencilEnable = desc.stencilEnabled; CopyStencilSide(d3ddesc.FrontFace, desc.front); CopyStencilSide(d3ddesc.BackFace, desc.back); if (SUCCEEDED(device_->CreateDepthStencilState(&d3ddesc, &ds->dss))) return ds; delete ds; return nullptr; } static const D3D11_BLEND_OP blendOpToD3D11[] = { D3D11_BLEND_OP_ADD, D3D11_BLEND_OP_SUBTRACT, D3D11_BLEND_OP_REV_SUBTRACT, D3D11_BLEND_OP_MIN, D3D11_BLEND_OP_MAX, }; static const D3D11_BLEND blendToD3D11[] = { D3D11_BLEND_ZERO, D3D11_BLEND_ONE, D3D11_BLEND_SRC_COLOR, D3D11_BLEND_INV_SRC_COLOR, D3D11_BLEND_DEST_COLOR, D3D11_BLEND_INV_DEST_COLOR, D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_DEST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA, D3D11_BLEND_BLEND_FACTOR, D3D11_BLEND_INV_BLEND_FACTOR, D3D11_BLEND_BLEND_FACTOR, D3D11_BLEND_INV_BLEND_FACTOR, D3D11_BLEND_SRC1_COLOR, D3D11_BLEND_INV_SRC1_COLOR, D3D11_BLEND_SRC1_ALPHA, D3D11_BLEND_INV_SRC1_ALPHA, }; class D3D11BlendState : public BlendState { public: ~D3D11BlendState() { bs->Release(); } ID3D11BlendState *bs; }; BlendState *D3D11DrawContext::CreateBlendState(const BlendStateDesc &desc) { D3D11BlendState *bs = new D3D11BlendState(); D3D11_BLEND_DESC d3ddesc{}; d3ddesc.AlphaToCoverageEnable = FALSE; d3ddesc.IndependentBlendEnable = FALSE; d3ddesc.RenderTarget[0].BlendEnable = desc.enabled; d3ddesc.RenderTarget[0].RenderTargetWriteMask = desc.colorMask; d3ddesc.RenderTarget[0].BlendOp = blendOpToD3D11[(int)desc.eqCol]; d3ddesc.RenderTarget[0].BlendOpAlpha = blendOpToD3D11[(int)desc.eqAlpha]; d3ddesc.RenderTarget[0].SrcBlend = blendToD3D11[(int)desc.srcCol]; d3ddesc.RenderTarget[0].SrcBlendAlpha = blendToD3D11[(int)desc.srcAlpha]; d3ddesc.RenderTarget[0].DestBlend = blendToD3D11[(int)desc.dstCol]; d3ddesc.RenderTarget[0].DestBlendAlpha = blendToD3D11[(int)desc.dstAlpha]; if (SUCCEEDED(device_->CreateBlendState(&d3ddesc, &bs->bs))) return bs; delete bs; return nullptr; } class D3D11RasterState : public RasterState { public: ~D3D11RasterState() { rs->Release(); } ID3D11RasterizerState *rs; }; RasterState *D3D11DrawContext::CreateRasterState(const RasterStateDesc &desc) { D3D11RasterState *rs = new D3D11RasterState(); D3D11_RASTERIZER_DESC d3ddesc{}; d3ddesc.FillMode = D3D11_FILL_SOLID; switch (desc.cull) { case CullMode::BACK: d3ddesc.CullMode = D3D11_CULL_BACK; break; case CullMode::FRONT: d3ddesc.CullMode = D3D11_CULL_FRONT; break; default: case CullMode::NONE: d3ddesc.CullMode = D3D11_CULL_NONE; break; } d3ddesc.FrontCounterClockwise = desc.frontFace == Facing::CCW; if (SUCCEEDED(device_->CreateRasterizerState(&d3ddesc, &rs->rs))) return rs; delete rs; return nullptr; } class D3D11SamplerState : public SamplerState { public: ~D3D11SamplerState() { ss->Release(); } ID3D11SamplerState *ss; }; static const D3D11_TEXTURE_ADDRESS_MODE taddrToD3D11[] = { D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_MIRROR, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_BORDER, }; SamplerState *D3D11DrawContext::CreateSamplerState(const SamplerStateDesc &desc) { D3D11SamplerState *ss = new D3D11SamplerState(); D3D11_SAMPLER_DESC d3ddesc{}; d3ddesc.AddressU = taddrToD3D11[(int)desc.wrapU]; d3ddesc.AddressV = taddrToD3D11[(int)desc.wrapV]; d3ddesc.AddressW = taddrToD3D11[(int)desc.wrapW]; // TODO: Needs improvement d3ddesc.Filter = desc.magFilter == TextureFilter::LINEAR ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_MIP_POINT; d3ddesc.MaxAnisotropy = (UINT)desc.maxAniso; d3ddesc.ComparisonFunc = compareToD3D11[(int)desc.shadowCompareFunc]; if (SUCCEEDED(device_->CreateSamplerState(&d3ddesc, &ss->ss))) return ss; delete ss; return nullptr; } // Input layout creation is delayed to pipeline creation, as we need the vertex shader bytecode. class D3D11InputLayout : public InputLayout { public: D3D11InputLayout() {} InputLayoutDesc desc; std::vector elements; std::vector strides; }; const char *semanticToD3D11(int semantic, UINT *index) { *index = 0; switch (semantic) { case SEM_POSITION: return "POSITION"; case SEM_COLOR0: *index = 0; return "COLOR"; case SEM_TEXCOORD0: *index = 0; return "TEXCOORD"; case SEM_TEXCOORD1: *index = 1; return "TEXCOORD"; case SEM_NORMAL: return "NORMAL"; case SEM_TANGENT: return "TANGENT"; case SEM_BINORMAL: return "BINORMAL"; // really BITANGENT default: return "UNKNOWN"; } } InputLayout *D3D11DrawContext::CreateInputLayout(const InputLayoutDesc &desc) { D3D11InputLayout *inputLayout = new D3D11InputLayout(); inputLayout->desc = desc; // Translate to D3D11 elements; for (size_t i = 0; i < desc.attributes.size(); i++) { D3D11_INPUT_ELEMENT_DESC el; el.AlignedByteOffset = desc.attributes[i].offset; el.Format = dataFormatToD3D11(desc.attributes[i].format); el.InstanceDataStepRate = desc.bindings[desc.attributes[i].binding].instanceRate ? 1 : 0; el.InputSlot = desc.attributes[i].binding; el.SemanticName = semanticToD3D11(desc.attributes[i].location, &el.SemanticIndex); el.InputSlotClass = desc.bindings[desc.attributes[i].binding].instanceRate ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; inputLayout->elements.push_back(el); } for (size_t i = 0; i < desc.bindings.size(); i++) { inputLayout->strides.push_back(desc.bindings[i].stride); } return inputLayout; } class D3D11Pipeline : public Pipeline { public: ~D3D11Pipeline() { input->Release(); blend->Release(); depth->Release(); raster->Release(); il->Release(); } // TODO: Refactor away these. void SetVector(const char *name, float *value, int n) { } void SetMatrix4x4(const char *name, const float value[16]) { } // pshaders don't usually have matrices bool RequiresBuffer() { return true; } D3D11InputLayout *input; ID3D11InputLayout *il = nullptr; D3D11BlendState *blend; D3D11DepthStencilState *depth; D3D11RasterState *raster; }; class D3D11Texture : public Texture { public: D3D11Texture() {} void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) override; }; Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { D3D11Texture *tex = new D3D11Texture(); // .... return tex; } class D3D11ShaderModule : public ShaderModule { public: std::vector byteCode_; }; ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize) { // ... return nullptr; } Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) { D3D11Pipeline *dPipeline = new D3D11Pipeline(); dPipeline->blend = (D3D11BlendState *)desc.blend; dPipeline->depth = (D3D11DepthStencilState *)desc.depthStencil; dPipeline->input = (D3D11InputLayout *)desc.inputLayout; dPipeline->raster = (D3D11RasterState *)desc.raster; dPipeline->blend->AddRef(); dPipeline->depth->AddRef(); dPipeline->input->AddRef(); dPipeline->raster->AddRef(); std::vector shaders; D3D11ShaderModule *vshader = nullptr; for (auto iter : desc.shaders) { shaders.push_back((D3D11ShaderModule *)iter); if (iter->GetStage() == ShaderStage::VERTEX) vshader = (D3D11ShaderModule *)iter; } if (!vshader) { // No vertex shader - no graphics dPipeline->Release(); return nullptr; } // Can finally create the input layout auto &inputDesc = dPipeline->input->desc; const std::vector &elements = dPipeline->input->elements; device_->CreateInputLayout(elements.data(), (UINT)elements.size(), vshader->byteCode_.data(), vshader->byteCode_.size(), &dPipeline->il); return dPipeline; } void D3D11DrawContext::BindPipeline(Pipeline *pipeline) { D3D11Pipeline *dPipeline = (D3D11Pipeline *)pipeline; curPipeline_ = dPipeline; } void D3D11DrawContext::ApplyCurrentState() { if (curBlend_ != curPipeline_->blend || blendFactorDirty_) { context_->OMSetBlendState(curPipeline_->blend->bs, blendFactor_, 0); curBlend_ = curPipeline_->blend; blendFactorDirty_ = false; } if (curDepth_ != curPipeline_->depth || stencilRefDirty_) { context_->OMSetDepthStencilState(curPipeline_->depth->dss, stencilRef_); curDepth_ = curPipeline_->depth; stencilRefDirty_ = false; } if (curRaster_ != curPipeline_->raster) { context_->RSSetState(curPipeline_->raster->rs); curRaster_ = curPipeline_->raster; } if (curInputLayout_ != curPipeline_->il) { context_->IASetInputLayout(curPipeline_->il); curInputLayout_ = curPipeline_->il; } } class D3D11Buffer : public Buffer { public: ID3D11Buffer *buf; virtual void SetData(const uint8_t *data, size_t size) override { } virtual void SubData(const uint8_t *data, size_t offset, size_t size) override { } }; Buffer *D3D11DrawContext::CreateBuffer(size_t size, uint32_t usageFlags) { D3D11Buffer *b = new D3D11Buffer(); return b; } void D3D11DrawContext::BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) { } void D3D11DrawContext::BindIndexBuffer(Buffer *indexBuffer, int offset) { } void D3D11DrawContext::Draw(int vertexCount, int offset) { ApplyCurrentState(); } void D3D11DrawContext::DrawIndexed(int vertexCount, int offset) { ApplyCurrentState(); } void D3D11DrawContext::DrawUP(const void *vdata, int vertexCount) { ApplyCurrentState(); } uint32_t D3D11DrawContext::GetDataFormatSupport(DataFormat fmt) const { // TODO: Actually do proper checks switch (fmt) { case DataFormat::B8G8R8A8_UNORM: return FMT_RENDERTARGET | FMT_TEXTURE; // D3D11 has no support for 4-bit component formats. case DataFormat::B4G4R4A4_UNORM_PACK16: case DataFormat::R4G4B4A4_UNORM_PACK16: case DataFormat::A4B4G4R4_UNORM_PACK16: return 0; case DataFormat::R8G8B8A8_UNORM: return FMT_RENDERTARGET | FMT_TEXTURE | FMT_INPUTLAYOUT; case DataFormat::R32_FLOAT: case DataFormat::R32G32_FLOAT: case DataFormat::R32G32B32_FLOAT: case DataFormat::R32G32B32A32_FLOAT: return FMT_INPUTLAYOUT; case DataFormat::R8_UNORM: return 0; case DataFormat::BC1_RGBA_UNORM_BLOCK: case DataFormat::BC2_UNORM_BLOCK: case DataFormat::BC3_UNORM_BLOCK: return FMT_TEXTURE; default: return 0; } } // A D3D11Framebuffer is a D3D11Framebuffer plus all the textures it owns. class D3D11Framebuffer : public Framebuffer { public: int width; int height; }; Framebuffer *D3D11DrawContext::CreateFramebuffer(const FramebufferDesc &desc) { D3D11Framebuffer *fb = new D3D11Framebuffer(); fb->width = desc.width; fb->height = desc.height; return fb; } void D3D11DrawContext::fbo_copy_image(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth) {} bool D3D11DrawContext::BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { return true; } int D3D11DrawContext::fbo_preferred_z_bitdepth() { return 24; } // These functions should be self explanatory. void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo) {} // color must be 0, for now. void D3D11DrawContext::fbo_bind_as_texture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) {} void D3D11DrawContext::fbo_bind_for_read(Framebuffer *fbo) {} void D3D11DrawContext::fbo_bind_backbuffer_as_render_target() {} uintptr_t D3D11DrawContext::fbo_get_api_texture(Framebuffer *fbo, int channelBit, int attachment) { return 0; } void D3D11DrawContext::fbo_get_dimensions(Framebuffer *fbo, int *w, int *h) { D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo; *w = fb->width; *h = fb->height; } #endif DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context) { return nullptr; // new D3D11DrawContext(device, context); } } // namespace Draw