void opengl_tnl_set_material(material* material_info, bool set_base_map, bool set_clipping) { int shader_handle = material_info->get_shader_handle(); int base_map = material_info->get_texture_map(TM_BASE_TYPE); vec4 clr = material_info->get_color(); Assert(shader_handle >= 0); opengl_shader_set_current(shader_handle); if (material_info->has_buffer_blend_modes()) { Assertion(GLAD_GL_ARB_draw_buffers_blend != 0, "Buffer blend modes are not supported at the moment! Query the capability before using this feature."); auto enable_blend = false; for (auto i = 0; i < (int) material::NUM_BUFFER_BLENDS; ++i) { auto mode = material_info->get_blend_mode(i); GL_state.SetAlphaBlendModei(i, mode); enable_blend = enable_blend || mode != ALPHA_BLEND_NONE; } GL_state.Blend(enable_blend ? GL_TRUE : GL_FALSE); } else { GL_state.SetAlphaBlendMode(material_info->get_blend_mode()); } GL_state.SetZbufferType(material_info->get_depth_mode()); gr_set_cull(material_info->get_cull_mode() ? 1 : 0); gr_zbias(material_info->get_depth_bias()); gr_set_fill_mode(material_info->get_fill_mode()); gr_set_texture_addressing(material_info->get_texture_addressing()); if (set_clipping) { // Only set the clipping state if explicitly requested by the caller to avoid unnecessary state changes auto& clip_params = material_info->get_clip_plane(); if (!clip_params.enabled) { GL_state.ClipDistance(0, false); } else { Assertion(Current_shader != NULL && (Current_shader->shader == SDR_TYPE_MODEL || Current_shader->shader == SDR_TYPE_PASSTHROUGH_RENDER || Current_shader->shader == SDR_TYPE_DEFAULT_MATERIAL), "Clip planes are not supported by this shader!"); GL_state.ClipDistance(0, true); } } GL_state.StencilMask(material_info->get_stencil_mask()); auto& stencilFunc = material_info->get_stencil_func(); GL_state.StencilFunc(convertComparisionFunction(stencilFunc.compare), stencilFunc.ref, stencilFunc.mask); auto& frontStencilOp = material_info->get_front_stencil_op(); GL_state.StencilOpSeparate(GL_FRONT, convertStencilOp(frontStencilOp.stencilFailOperation), convertStencilOp(frontStencilOp.depthFailOperation), convertStencilOp(frontStencilOp.successOperation)); auto& backStencilOp = material_info->get_back_stencil_op(); GL_state.StencilOpSeparate(GL_BACK, convertStencilOp(backStencilOp.stencilFailOperation), convertStencilOp(backStencilOp.depthFailOperation), convertStencilOp(backStencilOp.successOperation)); GL_state.StencilTest(material_info->is_stencil_enabled() ? GL_TRUE : GL_FALSE); auto& color_mask = material_info->get_color_mask(); GL_state.ColorMask(color_mask.x, color_mask.y, color_mask.z, color_mask.w); // This is only needed for the passthrough shader uint32_t array_index = 0; if ( set_base_map && base_map >= 0 ) { float u_scale, v_scale; if ( !gr_opengl_tcache_set(base_map, material_info->get_texture_type(), &u_scale, &v_scale, &array_index) ) { mprintf(("WARNING: Error setting bitmap texture (%i)!\n", base_map)); } } if ( Current_shader->shader == SDR_TYPE_DEFAULT_MATERIAL ) { opengl_shader_set_default_material(base_map >= 0, material_info->get_texture_type() == TCACHE_TYPE_AABITMAP, &clr, material_info->get_color_scale(), array_index, material_info->get_clip_plane()); } }
void PGRAPH::Begin(Primitive primitive) { // Set surface setSurface(); // Set viewport gfx::Viewport viewportRect = { viewport.x, viewport.y, viewport.width, viewport.height, 0.0f, 1.0f }; gfx::Rectangle scissorRect = { scissor.x, scissor.y, scissor.width, scissor.height }; cmdBuffer->cmdSetViewports(1, &viewportRect); cmdBuffer->cmdSetScissors(1, &scissorRect); // Hashing auto vpData = &vpe.data[vpe.start]; auto vpHash = HashVertexProgram(vpData); auto fpData = memory->ptr<rsx_fp_instruction_t>((fp_location ? rsx->get_ea(0x0) : 0xC0000000) + fp_offset); auto fpHash = HashFragmentProgram(fpData); auto pipelineHash = hashStruct(pipeline) ^ vpHash ^ fpHash; if (cachePipeline.find(pipelineHash) == cachePipeline.end()) { const auto& p = pipeline; if (cacheVP.find(vpHash) == cacheVP.end()) { auto vp = std::make_unique<RSXVertexProgram>(); vp->decompile(vpData); vp->compile(graphics.get()); cacheVP[vpHash] = std::move(vp); } if (cacheFP.find(fpHash) == cacheFP.end()) { auto fp = std::make_unique<RSXFragmentProgram>(); fp->decompile(fpData); fp->compile(graphics.get()); cacheFP[fpHash] = std::move(fp); } gfx::PipelineDesc pipelineDesc = {}; pipelineDesc.formatDSV = convertFormat(surface.depthFormat); pipelineDesc.numCBVs = 2; pipelineDesc.numSRVs = RSX_MAX_TEXTURES; pipelineDesc.vs = cacheVP[vpHash]->shader; pipelineDesc.ps = cacheFP[fpHash]->shader; pipelineDesc.rsState.fillMode = gfx::FILL_MODE_SOLID; pipelineDesc.rsState.cullMode = p.cull_face_enable ? convertCullMode(p.cull_mode) : gfx::CULL_MODE_NONE; pipelineDesc.rsState.frontCounterClockwise = convertFrontFace(p.front_face); pipelineDesc.rsState.depthEnable = p.depth_test_enable; pipelineDesc.rsState.depthWriteMask = p.depth_mask ? gfx::DEPTH_WRITE_MASK_ALL : gfx::DEPTH_WRITE_MASK_ZERO; pipelineDesc.rsState.depthFunc = convertCompareFunc(p.depth_func); pipelineDesc.rsState.stencilEnable = p.stencil_test_enable; pipelineDesc.rsState.stencilReadMask = p.stencil_func_mask; pipelineDesc.rsState.stencilWriteMask = p.stencil_mask; pipelineDesc.rsState.frontFace.stencilOpFail = convertStencilOp(p.stencil_op_fail); pipelineDesc.rsState.frontFace.stencilOpZFail = convertStencilOp(p.stencil_op_zfail); pipelineDesc.rsState.frontFace.stencilOpPass = convertStencilOp(p.stencil_op_zpass); pipelineDesc.rsState.frontFace.stencilFunc = convertCompareFunc(p.stencil_func); if (p.two_sided_stencil_test_enable) { pipelineDesc.rsState.backFace.stencilOpFail = convertStencilOp(p.stencil_op_fail); pipelineDesc.rsState.backFace.stencilOpZFail = convertStencilOp(p.stencil_op_zfail); pipelineDesc.rsState.backFace.stencilOpPass = convertStencilOp(p.stencil_op_zpass); pipelineDesc.rsState.backFace.stencilFunc = convertCompareFunc(p.stencil_func); } else { pipelineDesc.rsState.backFace.stencilOpFail = convertStencilOp(p.back_stencil_op_fail); pipelineDesc.rsState.backFace.stencilOpZFail = convertStencilOp(p.back_stencil_op_zfail); pipelineDesc.rsState.backFace.stencilOpPass = convertStencilOp(p.back_stencil_op_zpass); pipelineDesc.rsState.backFace.stencilFunc = convertCompareFunc(p.back_stencil_func); } pipelineDesc.cbState.colorTarget[0].enableBlend = p.blend_enable; pipelineDesc.cbState.colorTarget[0].enableLogicOp = p.logic_op_enable; pipelineDesc.cbState.colorTarget[0].blendOp = convertBlendOp(p.blend_equation_rgb); pipelineDesc.cbState.colorTarget[0].blendOpAlpha = convertBlendOp(p.blend_equation_alpha); pipelineDesc.cbState.colorTarget[0].srcBlend = convertBlend(p.blend_sfactor_rgb); pipelineDesc.cbState.colorTarget[0].destBlend = convertBlend(p.blend_dfactor_rgb); pipelineDesc.cbState.colorTarget[0].srcBlendAlpha = convertBlend(p.blend_sfactor_alpha); pipelineDesc.cbState.colorTarget[0].destBlendAlpha = convertBlend(p.blend_dfactor_alpha); pipelineDesc.cbState.colorTarget[0].colorWriteMask = convertColorMask(p.color_mask); pipelineDesc.cbState.colorTarget[0].logicOp = convertLogicOp(p.logic_op); pipelineDesc.iaState.topology = convertPrimitiveTopology(primitive); for (U32 index = 0; index < RSX_MAX_VERTEX_INPUTS; index++) { const auto& attr = vpe.attr[index]; if (!attr.size) { continue; } gfx::Format format = convertVertexFormat(attr.type, attr.size); U32 stride = attr.stride; pipelineDesc.iaState.inputLayout.push_back({ index, format, index, 0, stride, 0, gfx::INPUT_CLASSIFICATION_PER_VERTEX, 0 } ); } for (U32 i = 0; i < RSX_MAX_TEXTURES; i++) { gfx::Sampler sampler = {}; sampler.filter = gfx::FILTER_MIN_MAG_MIP_LINEAR; sampler.addressU = gfx::TEXTURE_ADDRESS_MIRROR; sampler.addressV = gfx::TEXTURE_ADDRESS_MIRROR; sampler.addressW = gfx::TEXTURE_ADDRESS_MIRROR; pipelineDesc.samplers.push_back(sampler); } cachePipeline[pipelineHash] = std::unique_ptr<gfx::Pipeline>(graphics->createPipeline(pipelineDesc)); } heapResources->reset(); heapResources->pushVertexBuffer(vpeConstantMemory); heapResources->pushVertexBuffer(vtxTransform); // Upload VPE constants if necessary void* constantsPtr = vpeConstantMemory->map(); memcpy(constantsPtr, &vpe.constant, sizeof(vpe.constant)); vpeConstantMemory->unmap(); // Upload vertex transform matrix if necessary if (vertex_transform_dirty) { V128* transformPtr = reinterpret_cast<V128*>(vtxTransform->map()); memset(transformPtr, 0, 4 * sizeof(V128)); F32 half_cliph = surface.width / 2.0f; F32 half_clipv = surface.height / 2.0f; transformPtr[0].f32[0] = (viewport_scale.f32[0] / half_cliph); transformPtr[1].f32[1] = (viewport_scale.f32[1] / half_clipv); transformPtr[2].f32[2] = (viewport_scale.f32[2]); transformPtr[0].f32[3] = (viewport_offset.f32[0] - half_cliph) / half_cliph; transformPtr[1].f32[3] = (viewport_offset.f32[1] - half_clipv) / half_clipv; transformPtr[2].f32[3] = (viewport_offset.f32[2]); transformPtr[3].f32[3] = 1.0f; vtxTransform->unmap(); } // Set textures for (U32 i = 0; i < RSX_MAX_TEXTURES; i++) { const auto& tex = texture[i]; // Dummy texture if (!tex.enable) { gfx::TextureDesc texDesc = {}; texDesc.width = 2; texDesc.height = 2; texDesc.format = gfx::FORMAT_R8G8B8A8_UNORM; texDesc.mipmapLevels = 1; texDesc.swizzle = TEXTURE_SWIZZLE_ENCODE( gfx::TEXTURE_SWIZZLE_VALUE_0, gfx::TEXTURE_SWIZZLE_VALUE_0, gfx::TEXTURE_SWIZZLE_VALUE_0, gfx::TEXTURE_SWIZZLE_VALUE_0 ); gfx::Texture* texDescriptor = graphics->createTexture(texDesc); heapResources->pushTexture(texDescriptor); } // Upload real texture else { auto texFormat = static_cast<TextureFormat>(tex.format & ~RSX_TEXTURE_LN & ~RSX_TEXTURE_UN); gfx::TextureDesc texDesc = {}; texDesc.data = memory->ptr<Byte>((tex.location ? rsx->get_ea(0x0) : 0xC0000000) + tex.offset); texDesc.size = tex.width * tex.height; texDesc.width = tex.width; texDesc.height = tex.height; texDesc.format = convertTextureFormat(texFormat); texDesc.mipmapLevels = tex.mipmap; texDesc.swizzle = convertTextureSwizzle(texFormat); switch (texFormat) { case RSX_TEXTURE_B8: texDesc.size *= 1; break; case RSX_TEXTURE_A1R5G5B5: texDesc.size *= 2; break; case RSX_TEXTURE_A4R4G4B4: texDesc.size *= 2; break; case RSX_TEXTURE_R5G6B5: texDesc.size *= 2; break; case RSX_TEXTURE_A8R8G8B8: texDesc.size *= 4; break; default: assert_always("Unimplemented"); } gfx::Texture* texDescriptor = graphics->createTexture(texDesc); heapResources->pushTexture(texDescriptor); } } cmdBuffer->cmdBindPipeline(cachePipeline[pipelineHash].get()); cmdBuffer->cmdSetHeaps({ heapResources }); cmdBuffer->cmdSetDescriptor(0, heapResources, 0); cmdBuffer->cmdSetDescriptor(1, heapResources, 2); cmdBuffer->cmdSetPrimitiveTopology(convertPrimitiveTopology(primitive)); }
Pipeline* Direct3D12Backend::createPipeline(const PipelineDesc& desc) { HRESULT hr; auto* pipeline = new Direct3D12Pipeline(); // Root signature parameters CD3DX12_DESCRIPTOR_RANGE ranges[2]; ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, desc.numCBVs, 0); ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, desc.numSRVs, 0); std::vector<CD3DX12_ROOT_PARAMETER> parameters; if (desc.numCBVs) { CD3DX12_ROOT_PARAMETER parameter; parameter.InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_ALL); parameters.push_back(parameter); } if (desc.numSRVs) { CD3DX12_ROOT_PARAMETER parameter; parameter.InitAsDescriptorTable(1, &ranges[1], D3D12_SHADER_VISIBILITY_PIXEL); parameters.push_back(parameter); } // Samplers std::vector<D3D12_STATIC_SAMPLER_DESC> d3dSamplers(desc.samplers.size()); for (Size i = 0; i < desc.samplers.size(); i++) { const auto& sampler = desc.samplers[i]; d3dSamplers[i].Filter = convertFilter(sampler.filter); d3dSamplers[i].AddressU = convertTextureAddressMode(sampler.addressU); d3dSamplers[i].AddressV = convertTextureAddressMode(sampler.addressV); d3dSamplers[i].AddressW = convertTextureAddressMode(sampler.addressW); d3dSamplers[i].MipLODBias = 0; d3dSamplers[i].MaxAnisotropy = 0; d3dSamplers[i].ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; d3dSamplers[i].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; d3dSamplers[i].MinLOD = 0.0f; d3dSamplers[i].MaxLOD = D3D12_FLOAT32_MAX; d3dSamplers[i].ShaderRegister = i; d3dSamplers[i].RegisterSpace = 0; d3dSamplers[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; } // Root signature D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {}; rootSignatureDesc.NumParameters = parameters.size(); rootSignatureDesc.pParameters = parameters.data(); rootSignatureDesc.NumStaticSamplers = d3dSamplers.size(); rootSignatureDesc.pStaticSamplers = d3dSamplers.data(); rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS; ID3DBlob* signature; ID3DBlob* error; hr = _D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error); if (FAILED(hr)) { LPVOID errorString = error->GetBufferPointer(); logger.error(LOG_GRAPHICS, "Direct3D12Backend::createPipeline: D3D12SerializeRootSignature failed (0x%X): %s", hr, errorString); return nullptr; } hr = device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&pipeline->rootSignature)); if (FAILED(hr)) { logger.error(LOG_GRAPHICS, "Direct3D12Backend::createPipeline: CreateRootSignature failed (0x%X)", hr); return nullptr; } D3D12_GRAPHICS_PIPELINE_STATE_DESC d3dDesc = {}; d3dDesc.NodeMask = 1; d3dDesc.SampleMask = UINT_MAX; d3dDesc.pRootSignature = pipeline->rootSignature; d3dDesc.NumRenderTargets = 1; d3dDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; d3dDesc.DSVFormat = convertFormat(desc.formatDSV); d3dDesc.SampleDesc.Count = 1; // Shaders if (desc.vs) { auto* d3dShader = static_cast<Direct3D12Shader*>(desc.vs); d3dDesc.VS.pShaderBytecode = d3dShader->bytecodeData; d3dDesc.VS.BytecodeLength = d3dShader->bytecodeSize; } if (desc.hs) { auto* d3dShader = static_cast<Direct3D12Shader*>(desc.hs); d3dDesc.HS.pShaderBytecode = d3dShader->bytecodeData; d3dDesc.HS.BytecodeLength = d3dShader->bytecodeSize; } if (desc.ds) { auto* d3dShader = static_cast<Direct3D12Shader*>(desc.ds); d3dDesc.DS.pShaderBytecode = d3dShader->bytecodeData; d3dDesc.DS.BytecodeLength = d3dShader->bytecodeSize; } if (desc.gs) { auto* d3dShader = static_cast<Direct3D12Shader*>(desc.gs); d3dDesc.GS.pShaderBytecode = d3dShader->bytecodeData; d3dDesc.GS.BytecodeLength = d3dShader->bytecodeSize; } if (desc.ps) { auto* d3dShader = static_cast<Direct3D12Shader*>(desc.ps); d3dDesc.PS.pShaderBytecode = d3dShader->bytecodeData; d3dDesc.PS.BytecodeLength = d3dShader->bytecodeSize; } // IA state std::vector<D3D12_INPUT_ELEMENT_DESC> d3dInputElements; for (const auto& element : desc.iaState.inputLayout) { DXGI_FORMAT format = convertFormat(element.format); D3D12_INPUT_CLASSIFICATION inputClassification = convertInputClassification(element.inputClassification); d3dInputElements.emplace_back(D3D12_INPUT_ELEMENT_DESC{ "INPUT", element.semanticIndex, format, element.inputSlot, element.offset, inputClassification, element.instanceStepRate }); } d3dDesc.InputLayout.NumElements = d3dInputElements.size(); d3dDesc.InputLayout.pInputElementDescs = d3dInputElements.data(); d3dDesc.PrimitiveTopologyType = convertPrimitiveTopologyType(desc.iaState.topology); // RS state d3dDesc.RasterizerState.FillMode = convertFillMode(desc.rsState.fillMode); d3dDesc.RasterizerState.CullMode = convertCullMode(desc.rsState.cullMode); d3dDesc.RasterizerState.FrontCounterClockwise = desc.rsState.frontCounterClockwise; d3dDesc.RasterizerState.DepthClipEnable = TRUE; // TODO d3dDesc.DepthStencilState.DepthEnable = desc.rsState.depthEnable; d3dDesc.DepthStencilState.DepthWriteMask = convertDepthWriteMask(desc.rsState.depthWriteMask); d3dDesc.DepthStencilState.DepthFunc = convertComparisonFunc(desc.rsState.depthFunc); d3dDesc.DepthStencilState.StencilEnable = desc.rsState.stencilEnable; d3dDesc.DepthStencilState.StencilReadMask = desc.rsState.stencilReadMask; d3dDesc.DepthStencilState.StencilWriteMask = desc.rsState.stencilWriteMask; d3dDesc.DepthStencilState.FrontFace.StencilFailOp = convertStencilOp(desc.rsState.frontFace.stencilOpFail); d3dDesc.DepthStencilState.FrontFace.StencilDepthFailOp = convertStencilOp(desc.rsState.frontFace.stencilOpZFail); d3dDesc.DepthStencilState.FrontFace.StencilPassOp = convertStencilOp(desc.rsState.frontFace.stencilOpPass); d3dDesc.DepthStencilState.FrontFace.StencilFunc = convertComparisonFunc(desc.rsState.frontFace.stencilFunc); d3dDesc.DepthStencilState.BackFace.StencilFailOp = convertStencilOp(desc.rsState.backFace.stencilOpFail); d3dDesc.DepthStencilState.BackFace.StencilDepthFailOp = convertStencilOp(desc.rsState.backFace.stencilOpZFail); d3dDesc.DepthStencilState.BackFace.StencilPassOp = convertStencilOp(desc.rsState.backFace.stencilOpPass); d3dDesc.DepthStencilState.BackFace.StencilFunc = convertComparisonFunc(desc.rsState.backFace.stencilFunc); // CB state d3dDesc.BlendState.AlphaToCoverageEnable = desc.cbState.enableAlphaToCoverage; d3dDesc.BlendState.IndependentBlendEnable = desc.cbState.enableIndependentBlend; UINT sizeColorTargetBlendArray = d3dDesc.BlendState.IndependentBlendEnable ? 8 : 1; for (UINT i = 0; i < sizeColorTargetBlendArray; i++) { const auto& source = desc.cbState.colorTarget[i]; auto& d3dTarget = d3dDesc.BlendState.RenderTarget[i]; d3dTarget.BlendEnable = source.enableBlend; d3dTarget.LogicOpEnable = source.enableLogicOp; d3dTarget.SrcBlend = convertBlend(source.srcBlend); d3dTarget.DestBlend = convertBlend(source.destBlend); d3dTarget.BlendOp = convertBlendOp(source.blendOp); d3dTarget.SrcBlendAlpha = convertBlend(source.srcBlendAlpha); d3dTarget.DestBlendAlpha = convertBlend(source.destBlendAlpha); d3dTarget.BlendOpAlpha = convertBlendOp(source.blendOpAlpha); d3dTarget.LogicOp = convertLogicOp(source.logicOp); d3dTarget.RenderTargetWriteMask = convertColorWriteMask(source.colorWriteMask); } hr = device->CreateGraphicsPipelineState(&d3dDesc, IID_PPV_ARGS(&pipeline->state)); if (FAILED(hr)) { logger.error(LOG_GRAPHICS, "Direct3D12Backend::createPipeline: CreateGraphicsPipelineState failed (0x%X)", hr); return nullptr; } return pipeline; }