#ifdef BLAH_USE_D3D11 // TODO: // Note the D3D11 Implementation is still a work-in-progress #include "../internal/graphics_backend.h" #include "../internal/platform_backend.h" #include #include #include #include #define WIN32_LEAN_AND_MEAN #include #include #include namespace Blah { class D3D11_Shader; struct D3D11 { // main resources ID3D11Device* device = nullptr; ID3D11DeviceContext* context = nullptr; IDXGISwapChain* swap_chain = nullptr; ID3D11RenderTargetView* backbuffer_view = nullptr; ID3D11DepthStencilView* backbuffer_depth_view = nullptr; // supported renderer features RendererFeatures features; // last backbuffer size Point last_size; struct StoredInputLayout { u32 shader_hash; VertexFormat format; ID3D11InputLayout* layout; }; struct StoredBlendState { BlendMode blend; ID3D11BlendState* state; }; struct StoredRasterizer { Cull cull; bool has_scissor; ID3D11RasterizerState* state; }; struct StoredSampler { TextureSampler sampler; ID3D11SamplerState* state; }; struct StoredDepthStencil { Compare depth; ID3D11DepthStencilState* state; }; Vector layout_cache; Vector blend_cache; Vector rasterizer_cache; Vector sampler_cache; Vector depthstencil_cache; ID3D11InputLayout* get_layout(D3D11_Shader* shader, const VertexFormat& format); ID3D11BlendState* get_blend(const BlendMode& blend); ID3D11RasterizerState* get_rasterizer(const RenderPass& pass); ID3D11SamplerState* get_sampler(const TextureSampler& sampler); ID3D11DepthStencilState* get_depthstencil(const RenderPass& pass); }; // D3D11 State D3D11 state; // Utility Methods D3D11_BLEND_OP blend_op(BlendOp op); D3D11_BLEND blend_factor(BlendFactor factor); bool reflect_uniforms(Vector& append_uniforms_to, Vector& append_buffers_to, ID3DBlob* shader, ShaderType shader_type); void apply_uniforms(D3D11_Shader* shader, const MaterialRef& material, ShaderType type); // ~ BEGIN IMPLEMENTATION ~ class D3D11_Texture : public Texture { private: int m_width; int m_height; TextureFormat m_format; DXGI_FORMAT m_dxgi_format; bool m_is_framebuffer; int m_size; public: ID3D11Texture2D* texture = nullptr; ID3D11Texture2D* staging = nullptr; ID3D11ShaderResourceView* view = nullptr; D3D11_Texture(int width, int height, TextureFormat format, bool is_framebuffer) { m_width = width; m_height = height; m_format = format; m_is_framebuffer = is_framebuffer; m_size = 0; D3D11_TEXTURE2D_DESC desc = { 0 }; desc.Width = width; desc.Height = height; desc.MipLevels = 1; desc.ArraySize = 1; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Usage = D3D11_USAGE_DEFAULT; desc.CPUAccessFlags = 0; desc.MiscFlags = 0; bool is_depth_stencil = false; switch (format) { case TextureFormat::R: desc.Format = DXGI_FORMAT_R8_UNORM; m_size = width * height; break; case TextureFormat::RG: desc.Format = DXGI_FORMAT_R8G8_UNORM; m_size = width * height * 2; break; case TextureFormat::RGBA: desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; m_size = width * height * 4; break; case TextureFormat::DepthStencil: desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; m_size = width * height * 4; is_depth_stencil = true; break; case TextureFormat::None: case TextureFormat::Count: break; } if (!is_depth_stencil) desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; else desc.BindFlags |= D3D11_BIND_DEPTH_STENCIL; if (is_framebuffer && !is_depth_stencil) desc.BindFlags |= D3D11_BIND_RENDER_TARGET; m_dxgi_format = desc.Format; auto hr = state.device->CreateTexture2D(&desc, NULL, &texture); if (!SUCCEEDED(hr)) { if (texture) texture->Release(); texture = nullptr; return; } if (!is_depth_stencil) { hr = state.device->CreateShaderResourceView(texture, NULL, &view); if (!SUCCEEDED(hr)) { texture->Release(); texture = nullptr; } } } ~D3D11_Texture() { if (texture) texture->Release(); if (staging) staging->Release(); if (view) view->Release(); staging = nullptr; texture = nullptr; view = nullptr; } virtual int width() const override { return m_width; } virtual int height() const override { return m_height; } virtual TextureFormat format() const override { return m_format; } virtual void set_data(unsigned char* data) override { // bounds D3D11_BOX box; box.left = 0; box.right = m_width; box.top = 0; box.bottom = m_height; box.front = 0; box.back = 1; // set data state.context->UpdateSubresource( texture, 0, &box, data, m_size / m_height, 0); } virtual void get_data(unsigned char* data) override { HRESULT hr; // create staging texture if (!staging) { D3D11_TEXTURE2D_DESC desc; desc.Width = m_width; desc.Height = m_height; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = m_dxgi_format; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Usage = D3D11_USAGE_STAGING; desc.BindFlags = 0; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.MiscFlags = 0; hr = state.device->CreateTexture2D(&desc, NULL, &staging); if (!SUCCEEDED(hr)) { BLAH_ERROR("Failed to create staging texture to get data"); return; } } // copy data to staging texture state.context->CopySubresourceRegion( staging, 0, 0, 0, 0, texture, 0, nullptr); // get data D3D11_MAPPED_SUBRESOURCE map; hr = state.context->Map(staging, 0, D3D11_MAP_READ, 0, &map); if (!SUCCEEDED(hr)) { BLAH_ERROR("Failed to get texture data"); return; } memcpy(data, map.pData, m_size); state.context->Unmap(staging, 0); } virtual bool is_framebuffer() const override { return m_is_framebuffer; } }; class D3D11_FrameBuffer : public FrameBuffer { private: Attachments m_attachments; public: StackVector color_views; ID3D11DepthStencilView* depth_view = nullptr; D3D11_FrameBuffer(int width, int height, const TextureFormat* attachments, int attachment_count) { for (int i = 0; i < attachment_count; i++) { auto tex = new D3D11_Texture(width, height, attachments[i], true); m_attachments.push_back(TextureRef(tex)); if (attachments[i] == TextureFormat::DepthStencil) { state.device->CreateDepthStencilView(tex->texture, nullptr, &depth_view); } else { ID3D11RenderTargetView* view = nullptr; state.device->CreateRenderTargetView(tex->texture, nullptr, &view); color_views.push_back(view); } } } ~D3D11_FrameBuffer() { for (auto& it : color_views) it->Release(); color_views.clear(); } virtual Attachments& attachments() override { return m_attachments; } virtual const Attachments& attachments() const override { return m_attachments; } virtual TextureRef& attachment(int index) override { return m_attachments[index]; } virtual const TextureRef& attachment(int index) const override { return m_attachments[index]; } virtual int width() const override { return m_attachments[0]->width(); } virtual int height() const override { return m_attachments[0]->height(); } virtual void clear(Color color, float depth, u8 stencil, ClearMask mask) override { float col[4] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color) { for (int i = 0; i < color_views.size(); i++) state.context->ClearRenderTargetView(color_views[i], col); } if (depth_view) { UINT flags = 0; if (((int)mask & (int)ClearMask::Depth) == (int)ClearMask::Depth) flags |= D3D11_CLEAR_DEPTH; if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil) flags |= D3D11_CLEAR_STENCIL; if (flags != 0) state.context->ClearDepthStencilView(depth_view, flags, depth, stencil); } } }; class D3D11_Shader : public Shader { public: ID3D11VertexShader* vertex = nullptr; ID3D11PixelShader* fragment = nullptr; ID3DBlob* vertex_blob = nullptr; ID3DBlob* fragment_blob = nullptr; Vector vertex_uniform_buffers; Vector fragment_uniform_buffers; Vector> vertex_uniform_values; Vector> fragment_uniform_values; StackVector attributes; Vector uniform_list; u32 hash = 0; bool valid = false; D3D11_Shader(const ShaderData* data) { UINT flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_DEBUG; ID3DBlob* error_blob = nullptr; HRESULT hr; // compile vertex shader { hr = D3DCompile( data->vertex.cstr(), data->vertex.length(), nullptr, nullptr, nullptr, "vs_main", "vs_5_0", flags, 0, &vertex_blob, &error_blob); if (FAILED(hr)) { Log::error("%s", (char*)error_blob->GetBufferPointer()); error_blob->Release(); return; } } // compile fragment shader { hr = D3DCompile( data->fragment.cstr(), data->fragment.length(), nullptr, nullptr, nullptr, "ps_main", "ps_5_0", flags, 0, &fragment_blob, &error_blob); if (FAILED(hr)) { Log::error("%s", (char*)error_blob->GetBufferPointer()); error_blob->Release(); return; } } // create vertex shader { hr = state.device->CreateVertexShader( vertex_blob->GetBufferPointer(), vertex_blob->GetBufferSize(), NULL, &vertex); if (!SUCCEEDED(hr)) return; } // create fragment shader { hr = state.device->CreatePixelShader( fragment_blob->GetBufferPointer(), fragment_blob->GetBufferSize(), NULL, &fragment); if (!SUCCEEDED(hr)) return; } // get uniforms reflect_uniforms(uniform_list, vertex_uniform_buffers, vertex_blob, ShaderType::Vertex); reflect_uniforms(uniform_list, fragment_uniform_buffers, fragment_blob, ShaderType::Fragment); // combine uniforms that were in both for (int i = 0; i < uniform_list.size(); i++) { for (int j = i + 1; j < uniform_list.size(); j++) { if (strcmp(uniform_list[i].name, uniform_list[j].name) == 0) { if (uniform_list[i].type == uniform_list[j].type) { uniform_list[i].shader = (ShaderType)((int)uniform_list[i].shader | (int)uniform_list[j].shader); uniform_list.erase(j); j--; } } } } // create CPU uniform buffers, so we don't need to create them during rendering vertex_uniform_values.expand(vertex_uniform_buffers.size()); fragment_uniform_values.expand(fragment_uniform_buffers.size()); // copy HLSL attributes attributes = data->hlsl_attributes; // store hash hash = 5381; for (auto& it : attributes) { for (size_t i = 0, n = strlen(it.semantic_name); i < n; i++) hash = ((hash << 5) + hash) + it.semantic_name[i]; hash = (it.semantic_index << 5) + hash; } // Shader is ready for use! valid = true; } ~D3D11_Shader() { if (vertex) vertex->Release(); if (vertex_blob) vertex_blob->Release(); if (fragment) fragment->Release(); if (fragment_blob) fragment_blob->Release(); for (auto& it : vertex_uniform_buffers) it->Release(); for (auto& it : fragment_uniform_buffers) it->Release(); vertex = nullptr; vertex_blob = nullptr; fragment = nullptr; fragment_blob = nullptr; } virtual Vector& uniforms() override { return uniform_list; } virtual const Vector& uniforms() const override { return uniform_list; } }; class D3D11_Mesh : public Mesh { private: i64 m_vertex_count = 0; i64 m_vertex_capacity = 0; i64 m_index_count = 0; i64 m_index_capacity = 0; public: ID3D11Buffer* vertex_buffer = nullptr; VertexFormat vertex_format; ID3D11Buffer* index_buffer = nullptr; IndexFormat index_format = IndexFormat::UInt16; int index_stride = 0; D3D11_Mesh() { } ~D3D11_Mesh() { if (vertex_buffer) vertex_buffer->Release(); if (index_buffer) index_buffer->Release(); } virtual void index_data(IndexFormat format, const void* indices, i64 count) override { m_index_count = count; if (index_format != format || !index_buffer || m_index_count > m_index_capacity) { index_stride = 0; index_format = format; m_index_capacity = max(m_index_capacity, m_index_count); switch (format) { case IndexFormat::UInt16: index_stride = sizeof(i16); break; case IndexFormat::UInt32: index_stride = sizeof(i32); break; } if (m_index_capacity > 0 && indices) { // release existing buffer if (index_buffer) index_buffer->Release(); index_buffer = nullptr; // buffer description D3D11_BUFFER_DESC desc = { 0 }; desc.ByteWidth = (UINT)(index_stride * m_index_capacity); desc.Usage = D3D11_USAGE_DYNAMIC; desc.BindFlags = D3D11_BIND_INDEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // buffer data D3D11_SUBRESOURCE_DATA data = { 0 }; data.pSysMem = indices; // create auto hr = state.device->CreateBuffer(&desc, &data, &index_buffer); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Index Data"); } } else if (indices) { D3D11_MAPPED_SUBRESOURCE map; auto hr = state.context->Map(index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Index Data"); if (SUCCEEDED(hr)) { memcpy(map.pData, indices, index_stride * count); state.context->Unmap(index_buffer, 0); } } } virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) override { m_vertex_count = count; // recreate buffer if we've changed if (vertex_format.stride != format.stride || !vertex_buffer || m_vertex_count > m_vertex_capacity) { m_vertex_capacity = max(m_vertex_capacity, m_vertex_count); vertex_format = format; // discard old buffer if (vertex_buffer) vertex_buffer->Release(); vertex_buffer = nullptr; if (m_vertex_capacity > 0 && vertices) { // buffer description D3D11_BUFFER_DESC desc = { 0 }; desc.ByteWidth = (UINT)(format.stride * m_vertex_capacity); desc.Usage = D3D11_USAGE_DYNAMIC; desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // buffer data D3D11_SUBRESOURCE_DATA data = { 0 }; data.pSysMem = vertices; // create auto hr = state.device->CreateBuffer(&desc, &data, &vertex_buffer); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Vertex Data"); } } // otherwise just update it else if (vertices) { D3D11_MAPPED_SUBRESOURCE map; auto hr = state.context->Map(vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Vertex Data"); if (SUCCEEDED(hr)) { memcpy(map.pData, vertices, vertex_format.stride * count); state.context->Unmap(vertex_buffer, 0); } } } virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) override { } virtual i64 index_count() const override { return m_index_count; } virtual i64 vertex_count() const override { return m_vertex_count; } virtual i64 instance_count() const override { return 0; } }; bool GraphicsBackend::init() { state = D3D11(); state.last_size = Point(App::draw_width(), App::draw_height()); // Define Swap Chain DXGI_SWAP_CHAIN_DESC desc = {}; desc.BufferDesc.RefreshRate.Numerator = 0; desc.BufferDesc.RefreshRate.Denominator = 1; desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.BufferCount = 1; desc.OutputWindow = (HWND)PlatformBackend::d3d11_get_hwnd(); desc.Windowed = true; // Creation Flags UINT flags = D3D11_CREATE_DEVICE_SINGLETHREADED; #if defined(DEBUG) || defined(_DEBUG) flags |= D3D11_CREATE_DEVICE_DEBUG; #endif // Create D3D device & context & swap cahin D3D_FEATURE_LEVEL feature_level; HRESULT hr = D3D11CreateDeviceAndSwapChain( NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, NULL, 0, D3D11_SDK_VERSION, &desc, &state.swap_chain, &state.device, &feature_level, &state.context); // Exit out if it's not OK if (!SUCCEEDED(hr) || !state.swap_chain || !state.device || !state.context) return false; // Get the backbuffer ID3D11Texture2D* frame_buffer = nullptr; state.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer); if (frame_buffer) { state.device->CreateRenderTargetView(frame_buffer, nullptr, &state.backbuffer_view); frame_buffer->Release(); } // TODO: // create a depth backbuffer // Store Features state.features.instancing = true; state.features.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; state.features.origin_bottom_left = false; // Print Driver Info { IDXGIDevice* dxgi_device; IDXGIAdapter* dxgi_adapter; DXGI_ADAPTER_DESC adapter_desc; hr = state.device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgi_device); if (SUCCEEDED(hr)) { dxgi_device->GetAdapter(&dxgi_adapter); dxgi_adapter->GetDesc(&adapter_desc); Log::info("D3D11 %ls", adapter_desc.Description); } else Log::info("D3D11"); } return true; } Renderer GraphicsBackend::renderer() { return Renderer::D3D11; } void GraphicsBackend::shutdown() { // release cached objects for (auto& it : state.blend_cache) it.state->Release(); for (auto& it : state.depthstencil_cache) it.state->Release(); for (auto& it : state.layout_cache) it.layout->Release(); for (auto& it : state.rasterizer_cache) it.state->Release(); for (auto& it : state.sampler_cache) it.state->Release(); // TODO: // Do we need to release live resources? ex. Texture's that // haven't been released by shutdown will still exist... // release main devices state.swap_chain->Release(); state.context->Release(); state.device->Release(); // reset state state = D3D11(); } const RendererFeatures& GraphicsBackend::features() { return state.features; } void GraphicsBackend::frame() { } void GraphicsBackend::before_render() { HRESULT hr; auto next_size = Point(App::draw_width(), App::draw_height()); if (state.last_size != next_size) { // release old buffer if (state.backbuffer_view) state.backbuffer_view->Release(); // perform resize hr = state.swap_chain->ResizeBuffers(0, next_size.x, next_size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Backbuffer on Resize"); state.last_size = next_size; // get the new buffer ID3D11Texture2D* frame_buffer = nullptr; hr = state.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer); if (SUCCEEDED(hr) && frame_buffer) { hr = state.device->CreateRenderTargetView(frame_buffer, nullptr, &state.backbuffer_view); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Backbuffer on Resize"); frame_buffer->Release(); } } } void GraphicsBackend::after_render() { auto hr = state.swap_chain->Present(1, 0); BLAH_ASSERT(SUCCEEDED(hr), "Failed to Present swap chain"); } TextureRef GraphicsBackend::create_texture(int width, int height, TextureFormat format) { auto result = new D3D11_Texture(width, height, format, false); if (result->texture) return TextureRef(result); delete result; return TextureRef(); } FrameBufferRef GraphicsBackend::create_framebuffer(int width, int height, const TextureFormat* attachments, int attachment_count) { return FrameBufferRef(new D3D11_FrameBuffer(width, height, attachments, attachment_count)); } ShaderRef GraphicsBackend::create_shader(const ShaderData* data) { auto result = new D3D11_Shader(data); if (result->valid) return ShaderRef(result); delete result; return ShaderRef(); } MeshRef GraphicsBackend::create_mesh() { return MeshRef(new D3D11_Mesh()); } void GraphicsBackend::render(const RenderPass& pass) { auto ctx = state.context; auto mesh = (D3D11_Mesh*)pass.mesh.get(); auto shader = (D3D11_Shader*)(pass.material->shader().get()); // OM { // Set the Target if (pass.target == App::backbuffer || !pass.target) { ctx->OMSetRenderTargets(1, &state.backbuffer_view, state.backbuffer_depth_view); } else { auto target = (D3D11_FrameBuffer*)(pass.target.get()); ctx->OMSetRenderTargets(target->color_views.size(), target->color_views.begin(), target->depth_view); } // Depth { auto depthstencil = state.get_depthstencil(pass); if (depthstencil) ctx->OMSetDepthStencilState(depthstencil, 0); } // Blend Mode { auto blend = state.get_blend(pass.blend); if (blend) { auto color = Color::from_rgba(pass.blend.rgba); float factor[4]{ color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; auto mask = 0xffffffff; ctx->OMSetBlendState(blend, factor, mask); } else { // if we failed to create a blend mode for some reason ctx->OMSetBlendState(nullptr, nullptr, 0); } } } // IA { // We draw triangles ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // Assign Layout auto layout = state.get_layout(shader, mesh->vertex_format); ctx->IASetInputLayout(layout); // Assign Vertex Buffer { UINT stride = mesh->vertex_format.stride; UINT offset = 0; ctx->IASetVertexBuffers( 0, 1, &mesh->vertex_buffer, &stride, &offset); D3D11_BUFFER_DESC desc; mesh->vertex_buffer->GetDesc(&desc); } // Assign Index Buffer { DXGI_FORMAT format = DXGI_FORMAT_R16_UINT; switch (mesh->index_format) { case IndexFormat::UInt16: format = DXGI_FORMAT_R16_UINT; break; case IndexFormat::UInt32: format = DXGI_FORMAT_R32_UINT; break; } ctx->IASetIndexBuffer(mesh->index_buffer, format, 0); } } // VS { apply_uniforms(shader, pass.material, ShaderType::Vertex); ctx->VSSetShader(shader->vertex, nullptr, 0); ctx->VSSetConstantBuffers(0, shader->vertex_uniform_buffers.size(), shader->vertex_uniform_buffers.begin()); } // PS { apply_uniforms(shader, pass.material, ShaderType::Fragment); ctx->PSSetShader(shader->fragment, nullptr, 0); ctx->PSSetConstantBuffers(0, shader->fragment_uniform_buffers.size(), shader->fragment_uniform_buffers.begin()); // Fragment Shader Textures auto& textures = pass.material->textures(); for (int i = 0; i < textures.size(); i++) { if (textures[i]) { // Assign the Texture auto view = ((D3D11_Texture*)textures[i].get())->view; ctx->PSSetShaderResources(i, 1, &view); } } // Fragment Shader Samplers auto& samplers = pass.material->samplers(); for (int i = 0; i < samplers.size(); i++) { auto sampler = state.get_sampler(samplers[i]); if (sampler) ctx->PSSetSamplers(i, 1, &sampler); } } // RS { // Set the Viewport { D3D11_VIEWPORT viewport = { pass.viewport.x, pass.viewport.y, pass.viewport.w, pass.viewport.h, 0.0f, 1.0f }; ctx->RSSetViewports(1, &viewport); } // Scissor Rect if (pass.has_scissor) { D3D11_RECT scissor = { (int)pass.scissor.x, (int)pass.scissor.y, (int)pass.scissor.x + (int)pass.scissor.w, (int)pass.scissor.y + (int)pass.scissor.h }; ctx->RSSetScissorRects(1, &scissor); } else { ctx->RSSetScissorRects(0, nullptr); } // Rasterizer { auto rasterizer = state.get_rasterizer(pass); if (rasterizer) ctx->RSSetState(rasterizer); } } // Draw { if (mesh->instance_count() <= 0) { ctx->DrawIndexed( static_cast(pass.index_count), static_cast(pass.index_start), 0); } else { // TODO: // Draw Instanced data BLAH_ASSERT(false, "Instanced Drawing not implemented yet"); } } // UnBind Shader Resources { auto& textures = pass.material->textures(); ID3D11ShaderResourceView* view = nullptr; for (int i = 0; i < textures.size(); i++) ctx->PSSetShaderResources(i, 1, &view); } } void GraphicsBackend::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) { if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color) { float clear[4] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; state.context->ClearRenderTargetView(state.backbuffer_view, clear); } if (state.backbuffer_depth_view) { UINT flags = 0; if (((int)mask & (int)ClearMask::Depth) == (int)ClearMask::Depth) flags |= D3D11_CLEAR_DEPTH; if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil) flags |= D3D11_CLEAR_STENCIL; if (flags != 0) state.context->ClearDepthStencilView(state.backbuffer_depth_view, flags, depth, stencil); } } // Utility Methods D3D11_BLEND_OP blend_op(BlendOp op) { switch (op) { case BlendOp::Add: return D3D11_BLEND_OP_ADD; case BlendOp::Subtract: return D3D11_BLEND_OP_SUBTRACT; case BlendOp::ReverseSubtract: return D3D11_BLEND_OP_REV_SUBTRACT; case BlendOp::Min: return D3D11_BLEND_OP_MIN; case BlendOp::Max: return D3D11_BLEND_OP_MAX; } return D3D11_BLEND_OP_ADD; } D3D11_BLEND blend_factor(BlendFactor factor) { switch (factor) { case BlendFactor::Zero: return D3D11_BLEND_ZERO; case BlendFactor::One: return D3D11_BLEND_ONE; case BlendFactor::SrcColor: return D3D11_BLEND_SRC_COLOR; case BlendFactor::OneMinusSrcColor: return D3D11_BLEND_INV_SRC_COLOR; case BlendFactor::DstColor: return D3D11_BLEND_DEST_COLOR; case BlendFactor::OneMinusDstColor: return D3D11_BLEND_INV_DEST_COLOR; case BlendFactor::SrcAlpha: return D3D11_BLEND_SRC_ALPHA; case BlendFactor::OneMinusSrcAlpha: return D3D11_BLEND_INV_SRC_ALPHA; case BlendFactor::DstAlpha: return D3D11_BLEND_DEST_ALPHA; case BlendFactor::OneMinusDstAlpha: return D3D11_BLEND_INV_DEST_ALPHA; case BlendFactor::ConstantColor: return D3D11_BLEND_BLEND_FACTOR; case BlendFactor::OneMinusConstantColor: return D3D11_BLEND_INV_BLEND_FACTOR; case BlendFactor::ConstantAlpha: return D3D11_BLEND_BLEND_FACTOR; case BlendFactor::OneMinusConstantAlpha: return D3D11_BLEND_INV_BLEND_FACTOR; case BlendFactor::SrcAlphaSaturate: return D3D11_BLEND_SRC_ALPHA_SAT; case BlendFactor::Src1Color: return D3D11_BLEND_SRC1_COLOR; case BlendFactor::OneMinusSrc1Color: return D3D11_BLEND_INV_SRC1_COLOR; case BlendFactor::Src1Alpha: return D3D11_BLEND_SRC1_ALPHA; case BlendFactor::OneMinusSrc1Alpha: return D3D11_BLEND_INV_SRC1_ALPHA; } return D3D11_BLEND_ZERO; } bool reflect_uniforms(Vector& append_uniforms_to, Vector& append_buffers_to, ID3DBlob* shader, ShaderType shader_type) { ID3D11ShaderReflection* reflector = nullptr; D3DReflect(shader->GetBufferPointer(), shader->GetBufferSize(), IID_ID3D11ShaderReflection, (void**)&reflector); D3D11_SHADER_DESC shader_desc; reflector->GetDesc(&shader_desc); for (UINT i = 0; i < shader_desc.BoundResources; i++) { D3D11_SHADER_INPUT_BIND_DESC desc; reflector->GetResourceBindingDesc(i, &desc); if (desc.Type == D3D_SIT_TEXTURE && desc.Dimension == D3D_SRV_DIMENSION_TEXTURE2D) { auto uniform = append_uniforms_to.expand(); uniform->name = desc.Name; uniform->shader = shader_type; uniform->buffer_index = 0; uniform->array_length = max(1, desc.BindCount); uniform->type = UniformType::Texture2D; } else if (desc.Type == D3D_SIT_SAMPLER) { auto uniform = append_uniforms_to.expand(); uniform->name = desc.Name; uniform->shader = shader_type; uniform->buffer_index = 0; uniform->array_length = max(1, desc.BindCount); uniform->type = UniformType::Sampler2D; } } for (UINT i = 0; i < shader_desc.ConstantBuffers; i++) { D3D11_SHADER_BUFFER_DESC desc; auto cb = reflector->GetConstantBufferByIndex(i); cb->GetDesc(&desc); // create the constant buffer for assigning data later { D3D11_BUFFER_DESC buffer_desc = {}; buffer_desc.ByteWidth = desc.Size; buffer_desc.Usage = D3D11_USAGE_DYNAMIC; buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; ID3D11Buffer* buffer; state.device->CreateBuffer(&buffer_desc, nullptr, &buffer); append_buffers_to.push_back(buffer); } // get the uniforms for (UINT j = 0; j < desc.Variables; j++) { D3D11_SHADER_VARIABLE_DESC var_desc; D3D11_SHADER_TYPE_DESC type_desc; auto var = cb->GetVariableByIndex(j); var->GetDesc(&var_desc); auto type = var->GetType(); type->GetDesc(&type_desc); auto uniform = append_uniforms_to.expand(); uniform->name = var_desc.Name; uniform->shader = shader_type; uniform->buffer_index = i; uniform->array_length = max(1, type_desc.Elements); uniform->type = UniformType::None; if (type_desc.Type == D3D_SVT_FLOAT) { if (type_desc.Rows == 1) { if (type_desc.Columns == 1) uniform->type = UniformType::Float; else if (type_desc.Columns == 2) uniform->type = UniformType::Float2; else if (type_desc.Columns == 3) uniform->type = UniformType::Float3; else if (type_desc.Columns == 4) uniform->type = UniformType::Float4; } else if (type_desc.Rows == 2 && type_desc.Columns == 3) { uniform->type = UniformType::Mat3x2; } else if (type_desc.Rows == 4 && type_desc.Columns == 4) { uniform->type = UniformType::Mat4x4; } } } } return true; } void apply_uniforms(D3D11_Shader* shader, const MaterialRef& material, ShaderType type) { auto& buffers = (type == ShaderType::Vertex ? shader->vertex_uniform_buffers : shader->fragment_uniform_buffers); auto& values = (type == ShaderType::Vertex ? shader->vertex_uniform_values : shader->fragment_uniform_values); for (int i = 0; i < buffers.size(); i++) { // clear previous values values[i].clear(); // build block const float* data = material->data(); for (auto& it : shader->uniforms()) { if (it.type == UniformType::None || it.type == UniformType::Texture2D || it.type == UniformType::Sampler2D) continue; int size = 0; switch (it.type) { case UniformType::None: break; case UniformType::Texture2D: break; case UniformType::Sampler2D: break; case UniformType::Float: size = 1; break; case UniformType::Float2: size = 2; break; case UniformType::Float3: size = 3; break; case UniformType::Float4: size = 4; break; case UniformType::Mat3x2: size = 6; break; case UniformType::Mat4x4: size = 16; break; } int length = size * it.array_length; if (it.buffer_index == i && ((int)it.shader & (int)type) != 0) { auto start = values[i].expand(length); memcpy(start, data, sizeof(float) * length); } data += length; } // apply block if (buffers[i]) { D3D11_MAPPED_SUBRESOURCE map; state.context->Map(buffers[i], 0, D3D11_MAP_WRITE_DISCARD, 0, &map); memcpy(map.pData, values[i].begin(), values[i].size() * sizeof(float)); state.context->Unmap(buffers[i], 0); } } } ID3D11InputLayout* D3D11::get_layout(D3D11_Shader* shader, const VertexFormat& format) { // find existing for (auto& it : layout_cache) { if (it.shader_hash == shader->hash && it.format.stride == format.stride && it.format.attributes.size() == format.attributes.size()) { bool same_format = true; for (int n = 0; same_format && n < format.attributes.size(); n++) if (it.format.attributes[n].index != format.attributes[n].index || it.format.attributes[n].type != format.attributes[n].type || it.format.attributes[n].normalized != format.attributes[n].normalized) same_format = false; if (same_format) return it.layout; } } // create a new one Vector desc; for (int i = 0; i < shader->attributes.size(); i++) { auto it = desc.expand(); it->SemanticName = shader->attributes[i].semantic_name; it->SemanticIndex = shader->attributes[i].semantic_index; if (!format.attributes[i].normalized) { switch (format.attributes[i].type) { case VertexType::None: break; case VertexType::Float: it->Format = DXGI_FORMAT_R32_FLOAT; break; case VertexType::Float2: it->Format = DXGI_FORMAT_R32G32_FLOAT; break; case VertexType::Float3: it->Format = DXGI_FORMAT_R32G32B32_FLOAT; break; case VertexType::Float4: it->Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break; case VertexType::Byte4: it->Format = DXGI_FORMAT_R8G8B8A8_SINT; break; case VertexType::UByte4: it->Format = DXGI_FORMAT_R8G8B8A8_UINT; break; case VertexType::Short2: it->Format = DXGI_FORMAT_R16G16_SINT; break; case VertexType::UShort2: it->Format = DXGI_FORMAT_R16G16_UINT; break; case VertexType::Short4: it->Format = DXGI_FORMAT_R16G16B16A16_SINT; break; case VertexType::UShort4: it->Format = DXGI_FORMAT_R16G16B16A16_UINT; break; } } else { switch (format.attributes[i].type) { case VertexType::None: break; case VertexType::Float: it->Format = DXGI_FORMAT_R32_FLOAT; break; case VertexType::Float2: it->Format = DXGI_FORMAT_R32G32_FLOAT; break; case VertexType::Float3: it->Format = DXGI_FORMAT_R32G32B32_FLOAT; break; case VertexType::Float4: it->Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break; case VertexType::Byte4: it->Format = DXGI_FORMAT_R8G8B8A8_SNORM; break; case VertexType::UByte4: it->Format = DXGI_FORMAT_R8G8B8A8_UNORM; break; case VertexType::Short2: it->Format = DXGI_FORMAT_R16G16_SNORM; break; case VertexType::UShort2: it->Format = DXGI_FORMAT_R16G16_UNORM; break; case VertexType::Short4: it->Format = DXGI_FORMAT_R16G16B16A16_SNORM; break; case VertexType::UShort4: it->Format = DXGI_FORMAT_R16G16B16A16_UNORM; break; } } it->InputSlot = 0; it->AlignedByteOffset = (i == 0 ? 0 : D3D11_APPEND_ALIGNED_ELEMENT); it->InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; it->InstanceDataStepRate = 0; } ID3D11InputLayout* layout = nullptr; auto hr = device->CreateInputLayout( desc.begin(), desc.size(), shader->vertex_blob->GetBufferPointer(), shader->vertex_blob->GetBufferSize(), &layout); if (SUCCEEDED(hr)) { auto entry = layout_cache.expand(); entry->shader_hash = shader->hash; entry->format = format; entry->layout = layout; return layout; } return nullptr; } ID3D11BlendState* D3D11::get_blend(const BlendMode& blend) { for (auto& it : blend_cache) if (it.blend == blend) return it.state; D3D11_BLEND_DESC desc = { 0 }; desc.AlphaToCoverageEnable = 0; desc.IndependentBlendEnable = 0; desc.RenderTarget[0].BlendEnable = !( blend.color_src == BlendFactor::One && blend.color_dst == BlendFactor::Zero && blend.alpha_src == BlendFactor::One && blend.alpha_dst == BlendFactor::Zero ); desc.RenderTarget[0].RenderTargetWriteMask = 0; if (((int)blend.mask & (int)BlendMask::Red) == (int)BlendMask::Red) desc.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_RED; if (((int)blend.mask & (int)BlendMask::Green) == (int)BlendMask::Green) desc.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_GREEN; if (((int)blend.mask & (int)BlendMask::Blue) == (int)BlendMask::Blue) desc.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_BLUE; if (((int)blend.mask & (int)BlendMask::Alpha) == (int)BlendMask::Alpha) desc.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_ALPHA; if (desc.RenderTarget[0].BlendEnable) { desc.RenderTarget[0].BlendOp = blend_op(blend.color_op); desc.RenderTarget[0].SrcBlend = blend_factor(blend.color_src); desc.RenderTarget[0].DestBlend = blend_factor(blend.color_dst); desc.RenderTarget[0].BlendOpAlpha = blend_op(blend.alpha_op); desc.RenderTarget[0].SrcBlendAlpha = blend_factor(blend.alpha_src); desc.RenderTarget[0].DestBlendAlpha = blend_factor(blend.alpha_dst); } for (int i = 1; i < 8; i ++) desc.RenderTarget[i] = desc.RenderTarget[0]; ID3D11BlendState* blend_state = nullptr; auto hr = state.device->CreateBlendState(&desc, &blend_state); if (SUCCEEDED(hr)) { auto entry = blend_cache.expand(); entry->blend = blend; entry->state = blend_state; return blend_state; } return nullptr; } ID3D11SamplerState* D3D11::get_sampler(const TextureSampler& sampler) { for (auto& it : sampler_cache) if (it.sampler == sampler) return it.state; D3D11_SAMPLER_DESC desc = {}; desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; desc.ComparisonFunc = D3D11_COMPARISON_NEVER; switch (sampler.filter) { case TextureFilter::None: break; case TextureFilter::Nearest: desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; break; case TextureFilter::Linear: desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; break; } switch (sampler.wrap_x) { case TextureWrap::None: break; case TextureWrap::Clamp: desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; break; case TextureWrap::Repeat: desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; break; } switch (sampler.wrap_y) { case TextureWrap::None: break; case TextureWrap::Clamp: desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; break; case TextureWrap::Repeat: desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; break; } ID3D11SamplerState* result; auto hr = state.device->CreateSamplerState(&desc, &result); if (SUCCEEDED(hr)) { auto entry = sampler_cache.expand(); entry->sampler = sampler; entry->state = result; return result; } return nullptr; } ID3D11RasterizerState* D3D11::get_rasterizer(const RenderPass& pass) { for (auto& it : rasterizer_cache) if (it.cull == pass.cull && it.has_scissor == pass.has_scissor) return it.state; D3D11_RASTERIZER_DESC desc = {}; desc.FillMode = D3D11_FILL_SOLID; desc.CullMode = D3D11_CULL_NONE; switch (pass.cull) { case Cull::None: desc.CullMode = D3D11_CULL_NONE; break; case Cull::Front: desc.CullMode = D3D11_CULL_FRONT; break; case Cull::Back: desc.CullMode = D3D11_CULL_BACK; break; } desc.FrontCounterClockwise = true; desc.DepthBias = 0; desc.DepthBiasClamp = 0; desc.SlopeScaledDepthBias = 0; desc.DepthClipEnable = false; desc.ScissorEnable = pass.has_scissor; desc.MultisampleEnable = false; desc.AntialiasedLineEnable = false; ID3D11RasterizerState* result; auto hr = state.device->CreateRasterizerState(&desc, &result); if (SUCCEEDED(hr)) { auto entry = rasterizer_cache.expand(); entry->cull = pass.cull; entry->has_scissor = pass.has_scissor; entry->state = result; return result; } return nullptr; } ID3D11DepthStencilState* D3D11::get_depthstencil(const RenderPass& pass) { for (auto& it : depthstencil_cache) if (it.depth == pass.depth) return it.state; D3D11_DEPTH_STENCIL_DESC desc = {}; desc.DepthEnable = pass.depth != Compare::None; desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; desc.DepthFunc = D3D11_COMPARISON_NEVER; switch (pass.depth) { case Compare::None: desc.DepthFunc = D3D11_COMPARISON_NEVER; break; case Compare::Always: desc.DepthFunc = D3D11_COMPARISON_ALWAYS; break; case Compare::Never: desc.DepthFunc = D3D11_COMPARISON_NEVER; break; case Compare::Less: desc.DepthFunc = D3D11_COMPARISON_LESS; break; case Compare::Equal: desc.DepthFunc = D3D11_COMPARISON_EQUAL; break; case Compare::LessOrEqual: desc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL; break; case Compare::Greater: desc.DepthFunc = D3D11_COMPARISON_GREATER; break; case Compare::NotEqual: desc.DepthFunc = D3D11_COMPARISON_NOT_EQUAL; break; case Compare::GreatorOrEqual: desc.DepthFunc = D3D11_COMPARISON_GREATER_EQUAL; break; } ID3D11DepthStencilState* result; auto hr = state.device->CreateDepthStencilState(&desc, &result); if (SUCCEEDED(hr)) { auto entry = depthstencil_cache.expand(); entry->depth = pass.depth; entry->state = result; return result; } return nullptr; } } #endif // BLAH_USE_D3D11