/* Copyright 2021 flyinghead This file is part of Flycast. Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Flycast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ #include "types.h" #include "hw/pvr/Renderer_if.h" #include #include "../dx11context.h" #include "../dx11_renderer.h" #include "rend/transform_matrix.h" #include "../dx11_texture.h" #include "dx11_oitshaders.h" #include "../dx11_renderstate.h" #include "dx11_oitbuffers.h" #include "dx11_oitshaders.h" #include "rend/tileclip.h" const D3D11_INPUT_ELEMENT_DESC MainLayout[] { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, (UINT)offsetof(Vertex, x), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0, (UINT)offsetof(Vertex, col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 1, DXGI_FORMAT_B8G8R8A8_UNORM, 0, (UINT)offsetof(Vertex, spc), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(Vertex, u), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 2, DXGI_FORMAT_B8G8R8A8_UNORM, 0, (UINT)offsetof(Vertex, col1), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 3, DXGI_FORMAT_B8G8R8A8_UNORM, 0, (UINT)offsetof(Vertex, spc1), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(Vertex, u1), D3D11_INPUT_PER_VERTEX_DATA, 0 }, // Naomi 2 { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, (UINT)offsetof(Vertex, nx), D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; struct DX11OITRenderer : public DX11Renderer { struct PixelPolyConstants { float clipTest[4]; int blend_mode0[2]; int blend_mode1[2]; float paletteIndex; float trilinearAlpha; // two volume mode int shading_instr0; int shading_instr1; int fog_control0; int fog_control1; int use_alpha0; int use_alpha1; int ignore_tex_alpha0; int ignore_tex_alpha1; }; bool Init() override { if (!DX11Renderer::Init()) return false; pxlPolyConstants.reset(); D3D11_BUFFER_DESC desc{}; desc.Usage = D3D11_USAGE_DYNAMIC; desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.ByteWidth = sizeof(PixelPolyConstants); desc.ByteWidth = (((desc.ByteWidth - 1) >> 4) + 1) << 4; bool success = SUCCEEDED(device->CreateBuffer(&desc, nullptr, &pxlPolyConstants.get())); shaders.init(device, theDX11Context.getCompiler()); buffers.init(device, deviceContext); pixelBufferSize = config::PixelBufferSize; ComPtr blob = shaders.getVertexShaderBlob(); mainInputLayout.reset(); success = SUCCEEDED(device->CreateInputLayout(MainLayout, ARRAY_SIZE(MainLayout), blob->GetBufferPointer(), blob->GetBufferSize(), &mainInputLayout.get())) && success; blob = shaders.getFinalVertexShaderBlob(); success = SUCCEEDED(device->CreateInputLayout(MainLayout, 0, blob->GetBufferPointer(), blob->GetBufferSize(), &finalInputLayout.get())) && success; desc.ByteWidth = sizeof(int); desc.ByteWidth = (((desc.ByteWidth - 1) >> 4) + 1) << 4; desc.Usage = D3D11_USAGE_DYNAMIC; desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; success = SUCCEEDED(device->CreateBuffer(&desc, nullptr, &vtxPolyConstants.get())) && success; return success; } void resize(int w, int h) override { if (w == (int)width && h == (int)height && opaqueTex != nullptr) return; DX11Renderer::resize(w, h); buffers.resize(w, h); createTexAndRenderTarget(opaqueTex, opaqueRenderTarget, width, height); multipassTex.reset(); multipassRenderTarget.reset(); multipassTextureView.reset(); opaqueTextureView.reset(); D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{}; viewDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; viewDesc.Texture2D.MipLevels = 1; device->CreateShaderResourceView(opaqueTex, &viewDesc, &opaqueTextureView.get()); // For depth pass. Use a 32-bit format for depth to avoid loss of precision createDepthTexAndView(depthStencilTex2, depthStencilView2, width, height, DXGI_FORMAT_R32G8X24_TYPELESS, D3D11_BIND_SHADER_RESOURCE); stencilView.reset(); viewDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT; device->CreateShaderResourceView(depthStencilTex2, &viewDesc, &stencilView.get()); depthView.reset(); viewDesc.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; device->CreateShaderResourceView(depthStencilTex2, &viewDesc, &depthView.get()); createDepthTexAndView(depthTex, depthTexView, width, height, DXGI_FORMAT_R32G8X24_TYPELESS); } void setRTTSize(int width, int height) override { buffers.resize(width, height); } void Term() override { vtxPolyConstants.reset(); finalInputLayout.reset(); mainInputLayout.reset(); multipassTextureView.reset(); multipassRenderTarget.reset(); multipassTex.reset(); opaqueTextureView.reset(); opaqueRenderTarget.reset(); opaqueTex.reset(); shaders.term(); buffers.term(); DX11Renderer::Term(); } template void setRenderState(const PolyParam *gp, int polyNumber) { ComPtr vertexShader = shaders.getVertexShader(gp->pcw.Gouraud, gp->isNaomi2(), false, pass != DX11OITShaders::Depth); deviceContext->VSSetShader(vertexShader, nullptr, 0); PixelPolyConstants constants; if (gp->pcw.Texture && gp->tsp.FilterMode > 1 && Type != ListType_Punch_Through && gp->tcw.MipMapped == 1) { constants.trilinearAlpha = 0.25f * (gp->tsp.MipMapD & 0x3); if (gp->tsp.FilterMode == 2) // Trilinear pass A constants.trilinearAlpha = 1.f - constants.trilinearAlpha; } else constants.trilinearAlpha = 1.f; int clip_rect[4] = {}; TileClipping clipmode = GetTileClip(gp->tileclip, matrices.GetViewportMatrix(), clip_rect); bool gpuPalette = gp->texture != nullptr ? gp->texture->gpuPalette : false; // Two volumes mode only supported for OP and PT bool two_volumes_mode = (gp->tsp1.full != (u32)-1) && Type != ListType_Translucent; bool useTexture; ComPtr pixelShader; if (pass == DX11OITShaders::Depth) { useTexture = Type == ListType_Punch_Through ? gp->pcw.Texture : false; pixelShader = shaders.getShader( useTexture, gp->tsp.UseAlpha, gp->tsp.IgnoreTexA || gp->tcw.PixelFmt == Pixel565, 0, false, 2, false, false, gpuPalette, gp->pcw.Gouraud, useTexture, clipmode == TileClipping::Inside, false, two_volumes_mode, pass); } else { bool color_clamp = gp->tsp.ColorClamp && (pvrrc.fog_clamp_min.full != 0 || pvrrc.fog_clamp_max.full != 0xffffffff); int fog_ctrl = config::Fog ? gp->tsp.FogCtrl : 2; useTexture = gp->pcw.Texture; pixelShader = shaders.getShader( useTexture, gp->tsp.UseAlpha, gp->tsp.IgnoreTexA || gp->tcw.PixelFmt == Pixel565, gp->tsp.ShadInstr, gp->pcw.Offset, fog_ctrl, gp->tcw.PixelFmt == PixelBumpMap, color_clamp, gpuPalette, gp->pcw.Gouraud, Type == ListType_Punch_Through, clipmode == TileClipping::Inside, gp->pcw.Texture && gp->tsp.FilterMode == 0 && !gp->tsp.ClampU && !gp->tsp.ClampV && !gp->tsp.FlipU && !gp->tsp.FlipV, two_volumes_mode, pass); } deviceContext->PSSetShader(pixelShader, nullptr, 0); if (gpuPalette) { if (gp->tcw.PixelFmt == PixelPal4) constants.paletteIndex = (float)(gp->tcw.PalSelect << 4); else constants.paletteIndex = (float)((gp->tcw.PalSelect >> 4) << 8); } if (clipmode == TileClipping::Outside) { RECT rect { clip_rect[0], clip_rect[1], clip_rect[0] + clip_rect[2], clip_rect[1] + clip_rect[3] }; deviceContext->RSSetScissorRects(1, &rect); } else { deviceContext->RSSetScissorRects(1, &scissorRect); if (clipmode == TileClipping::Inside) { constants.clipTest[0] = (float)clip_rect[0]; constants.clipTest[1] = (float)clip_rect[1]; constants.clipTest[2] = (float)(clip_rect[0] + clip_rect[2]); constants.clipTest[3] = (float)(clip_rect[1] + clip_rect[3]); } } constants.blend_mode0[0] = gp->tsp.SrcInstr; constants.blend_mode0[1] = gp->tsp.DstInstr; if (two_volumes_mode) { constants.blend_mode1[0] = gp->tsp1.SrcInstr; constants.blend_mode1[1] = gp->tsp1.DstInstr; constants.shading_instr0 = gp->tsp.ShadInstr; constants.shading_instr1 = gp->tsp1.ShadInstr; constants.fog_control0 = gp->tsp.FogCtrl; constants.fog_control1 = gp->tsp1.FogCtrl; constants.use_alpha0 = gp->tsp.UseAlpha; constants.use_alpha1 = gp->tsp1.UseAlpha; constants.ignore_tex_alpha0 = gp->tsp.IgnoreTexA; constants.ignore_tex_alpha1 = gp->tsp1.IgnoreTexA; } D3D11_MAPPED_SUBRESOURCE mappedSubres; deviceContext->Map(pxlPolyConstants, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubres); memcpy(mappedSubres.pData, &constants, sizeof(constants)); deviceContext->Unmap(pxlPolyConstants, 0); if (!gp->isNaomi2()) { deviceContext->Map(vtxPolyConstants, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubres); memcpy(mappedSubres.pData, &polyNumber, sizeof(polyNumber)); deviceContext->Unmap(vtxPolyConstants, 0); deviceContext->VSSetConstantBuffers(1, 1, &vtxPolyConstants.get()); } if (pass == DX11OITShaders::Color) { // Apparently punch-through polys support blending, or at least some combinations if (Type == ListType_Translucent || Type == ListType_Punch_Through) deviceContext->OMSetBlendState(blendStates.getState(true, gp->tsp.SrcInstr, gp->tsp.DstInstr), nullptr, 0xffffffff); else deviceContext->OMSetBlendState(blendStates.getState(false, gp->tsp.SrcInstr, gp->tsp.DstInstr), nullptr, 0xffffffff); } if (useTexture) { for (int i = 0; i < 2; i++) { DX11Texture *texture = (DX11Texture *)(i == 0 ? gp->texture : gp->texture1); if (texture == nullptr) continue; int slot = i == 0 ? 0 : 3; deviceContext->PSSetShaderResources(slot, 1, &texture->textureView.get()); TSP tsp = i == 0 ? gp->tsp : gp->tsp1; bool linearFiltering; if (config::TextureFiltering == 0) linearFiltering = tsp.FilterMode != 0 && !gpuPalette; else if (config::TextureFiltering == 1) linearFiltering = false; else linearFiltering = true; auto sampler = samplers->getSampler(linearFiltering, tsp.ClampU, tsp.ClampV, tsp.FlipU, tsp.FlipV); deviceContext->PSSetSamplers(slot, 1, &sampler.get()); } } setCullMode(gp->isp.CullMode); //set Z mode, only if required int zfunc; if (Type == ListType_Punch_Through || (pass == DX11OITShaders::Depth && SortingEnabled)) zfunc = 6; // GEQ else zfunc = gp->isp.DepthMode; bool zwriteEnable = false; if (pass == DX11OITShaders::Depth || pass == DX11OITShaders::Color) { // Z Write Disable seems to be ignored for punch-through. // Fixes Worms World Party, Bust-a-Move 4 and Re-Volt if (Type == ListType_Punch_Through) zwriteEnable = true; else zwriteEnable = !gp->isp.ZWriteDis; } bool needStencil = config::ModifierVolumes && pass == DX11OITShaders::Depth && Type != ListType_Translucent; const u32 stencil = (gp->pcw.Shadow != 0) ? 0x80 : 0; deviceContext->OMSetDepthStencilState(depthStencilStates.getState(true, zwriteEnable, zfunc, needStencil), stencil); if (gp->isNaomi2()) n2Helper.setConstants(*gp, polyNumber); } template void drawList(const List& gply, int first, int count) { deviceContext->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); PolyParam* params = &gply.head()[first]; u32 firstVertexIdx = Type == ListType_Translucent ? pvrrc.idx.head()[gply.head()->first] : 0; while (count-- > 0) { if (params->count > 2) { if ((Type == ListType_Opaque || (Type == ListType_Translucent && !SortingEnabled)) && params->isp.DepthMode == 0) { // depthFunc = never params++; continue; } setRenderState(params, (int)((params - gply.head()) << 17) - firstVertexIdx); deviceContext->DrawIndexed(params->count, params->first, 0); } params++; } } template void drawModVols(int first, int count, const ModifierVolumeParam *modVolParams) { if (count == 0 || pvrrc.modtrig.used() == 0 || !config::ModifierVolumes) return; deviceContext->IASetInputLayout(modVolInputLayout); unsigned int stride = 3 * sizeof(float); unsigned int offset = 0; deviceContext->IASetVertexBuffers(0, 1, &modvolBuffer.get(), &stride, &offset); deviceContext->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); if (!Transparent) deviceContext->PSSetShader(shaders.getModVolShader(), nullptr, 0); deviceContext->RSSetScissorRects(1, &scissorRect); const ModifierVolumeParam *params = &modVolParams[first]; int mod_base = -1; const float *curMVMat = nullptr; const float *curProjMat = nullptr; for (int cmv = 0; cmv < count; cmv++) { const ModifierVolumeParam& param = params[cmv]; u32 mv_mode = param.isp.DepthMode; if (mod_base == -1) mod_base = param.first; if (param.count > 0) { if (param.isNaomi2() && (param.mvMatrix != curMVMat || param.projMatrix != curProjMat)) { curMVMat = param.mvMatrix; curProjMat = param.projMatrix; n2Helper.setConstants(param.mvMatrix, param.projMatrix); } deviceContext->VSSetShader(shaders.getMVVertexShader(param.isNaomi2()), nullptr, 0); if (Transparent) { if (!param.isp.VolumeLast && mv_mode > 0) // OR'ing (open volume or quad) deviceContext->PSSetShader(shaders.getTrModVolShader(DepthStencilStates::Or), nullptr, 0); else // XOR'ing (closed volume) deviceContext->PSSetShader(shaders.getTrModVolShader(DepthStencilStates::Xor), nullptr, 0); } else { if (!param.isp.VolumeLast && mv_mode > 0) // OR'ing (open volume or quad) deviceContext->OMSetDepthStencilState(depthStencilStates.getMVState(DepthStencilStates::Or), 2); else // XOR'ing (closed volume) deviceContext->OMSetDepthStencilState(depthStencilStates.getMVState(DepthStencilStates::Xor), 0); } setCullMode(param.isp.CullMode); deviceContext->Draw(param.count * 3, param.first * 3); } if (mv_mode == 1 || mv_mode == 2) { // Sum the area if (Transparent) deviceContext->PSSetShader(shaders.getTrModVolShader(mv_mode == 1 ? DepthStencilStates::Inclusion : DepthStencilStates::Exclusion), nullptr, 0); else deviceContext->OMSetDepthStencilState(depthStencilStates.getMVState(mv_mode == 1 ? DepthStencilStates::Inclusion : DepthStencilStates::Exclusion), 1); deviceContext->Draw((param.first + param.count - mod_base) * 3, mod_base * 3); mod_base = -1; } } // Restore main input layout and vertex buffers deviceContext->IASetInputLayout(mainInputLayout); stride = sizeof(Vertex); offset = 0; deviceContext->IASetVertexBuffers(0, 1, &vertexBuffer.get(), &stride, &offset); deviceContext->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R32_UINT, 0); } void renderABuffer(bool lastPass) { if (!lastPass) deviceContext->OMSetRenderTargetsAndUnorderedAccessViews(1, &multipassRenderTarget.get(), nullptr, 0, D3D11_KEEP_UNORDERED_ACCESS_VIEWS, nullptr, nullptr); else if (pvrrc.isRTT) deviceContext->OMSetRenderTargetsAndUnorderedAccessViews(1, &rttRenderTarget.get(), nullptr, 0, D3D11_KEEP_UNORDERED_ACCESS_VIEWS, nullptr, nullptr); else deviceContext->OMSetRenderTargetsAndUnorderedAccessViews(1, &fbRenderTarget.get(), nullptr, 0, D3D11_KEEP_UNORDERED_ACCESS_VIEWS, nullptr, nullptr); deviceContext->OMSetBlendState(blendStates.getState(false), nullptr, 0xffffffff); deviceContext->PSSetShaderResources(0, 1, &opaqueTextureView.get()); auto sampler = samplers->getSampler(false); deviceContext->PSSetSamplers(0, 1, &sampler.get()); deviceContext->RSSetScissorRects(1, &scissorRect); deviceContext->OMSetDepthStencilState(depthStencilStates.getState(false, false, 0, false), 0); setCullMode(0); deviceContext->IASetInputLayout(finalInputLayout); deviceContext->VSSetShader(shaders.getFinalVertexShader(), nullptr, 0); deviceContext->PSSetShader(shaders.getFinalShader(), nullptr, 0); deviceContext->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); deviceContext->Draw(4, 0); } void drawStrips() { { // tr_poly_params std::vector trPolyParams(pvrrc.global_param_tr.used() * 2); const PolyParam *pp_end = pvrrc.global_param_tr.LastPtr(0); const PolyParam *pp = pvrrc.global_param_tr.head(); for (int i = 0; pp != pp_end; i += 2, pp++) { trPolyParams[i] = (pp->tsp.full & 0xffff00c0) | ((pp->isp.full >> 16) & 0xe400) | ((pp->pcw.full >> 7) & 1); trPolyParams[i + 1] = pp->tsp1.full; } u32 newSize = (u32)(trPolyParams.size() * sizeof(u32)); if (newSize > 0) { if (!trPolyParamsBuffer || trPolyParamsBufferSize < newSize) { trPolyParamsBufferView.reset(); trPolyParamsBuffer.reset(); D3D11_BUFFER_DESC desc{}; desc.ByteWidth = newSize; desc.Usage = D3D11_USAGE_DYNAMIC; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; desc.StructureByteStride = sizeof(u32) * 2; // sizeof(struct PolyParam) desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; HRESULT hr = device->CreateBuffer(&desc, nullptr, &trPolyParamsBuffer.get()); if (FAILED(hr)) WARN_LOG(RENDERER, "TR poly params buffer creation failed"); else { trPolyParamsBufferSize = newSize; D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{}; viewDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; viewDesc.Format = DXGI_FORMAT_UNKNOWN; viewDesc.Buffer.NumElements = desc.ByteWidth / desc.StructureByteStride; hr = device->CreateShaderResourceView(trPolyParamsBuffer, &viewDesc, &trPolyParamsBufferView.get()); if (FAILED(hr)) WARN_LOG(RENDERER, "TR poly params buffer view creation failed"); } } D3D11_MAPPED_SUBRESOURCE mappedSubres; deviceContext->Map(trPolyParamsBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubres); memcpy(mappedSubres.pData, trPolyParams.data(), newSize); deviceContext->Unmap(trPolyParamsBuffer, 0); deviceContext->PSSetShaderResources(5, 1, &trPolyParamsBufferView.get()); } } buffers.bind(); deviceContext->ClearDepthStencilView(depthTexView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 0.f, 0); deviceContext->ClearDepthStencilView(depthStencilView2, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 0.f, 0); RenderPass previous_pass {}; int render_pass_count = pvrrc.render_passes.used(); for (int render_pass = 0; render_pass < render_pass_count; render_pass++) { const RenderPass& current_pass = pvrrc.render_passes.head()[render_pass]; u32 op_count = current_pass.op_count - previous_pass.op_count; u32 pt_count = current_pass.pt_count - previous_pass.pt_count; u32 tr_count = current_pass.tr_count - previous_pass.tr_count; u32 mvo_count = current_pass.mvo_count - previous_pass.mvo_count; DEBUG_LOG(RENDERER, "Render pass %d OP %d PT %d TR %d MV %d Tr MV %d autosort %d", render_pass + 1, op_count, pt_count, tr_count, mvo_count, current_pass.mv_op_tr_shared ? mvo_count : current_pass.mvo_tr_count - previous_pass.mvo_tr_count, current_pass.autosort); // // PASS 1: Geometry pass to update depth and stencil // // unbind depth/stencil ID3D11ShaderResourceView *p = nullptr; deviceContext->PSSetShaderResources(4, 1, &p); // disable color writes deviceContext->OMSetBlendState(blendStates.getState(false, 0, 0, true), nullptr, 0xffffffff); deviceContext->OMSetRenderTargetsAndUnorderedAccessViews(1, &opaqueRenderTarget.get(), depthStencilView2, 0, D3D11_KEEP_UNORDERED_ACCESS_VIEWS, nullptr, nullptr); drawList(pvrrc.global_param_op, previous_pass.op_count, op_count); drawList(pvrrc.global_param_pt, previous_pass.pt_count, pt_count); drawModVols(previous_pass.mvo_count, mvo_count, pvrrc.global_param_mvo.head()); // // PASS 2: Render OP and PT to opaque render target // deviceContext->OMSetRenderTargetsAndUnorderedAccessViews(1, &opaqueRenderTarget.get(), depthTexView, 0, D3D11_KEEP_UNORDERED_ACCESS_VIEWS, nullptr, nullptr); deviceContext->PSSetShaderResources(4, 1, &stencilView.get()); drawList(pvrrc.global_param_op, previous_pass.op_count, op_count); drawList(pvrrc.global_param_pt, previous_pass.pt_count, pt_count); // // PASS 3: Render TR to a-buffers // if (current_pass.autosort) { deviceContext->PSSetShaderResources(4, 1, &depthView.get()); // disable color writes deviceContext->OMSetBlendState(blendStates.getState(false, 0, 0, true), nullptr, 0xffffffff); drawList(pvrrc.global_param_tr, previous_pass.tr_count, tr_count); if (render_pass < render_pass_count - 1) { // // PASS 3b: Geometry pass with TR to update the depth for the next TA render pass // ID3D11ShaderResourceView *p = nullptr; deviceContext->PSSetShaderResources(4, 1, &p); deviceContext->OMSetRenderTargetsAndUnorderedAccessViews(1, &opaqueRenderTarget.get(), depthTexView, 0, D3D11_KEEP_UNORDERED_ACCESS_VIEWS, nullptr, nullptr); drawList(pvrrc.global_param_tr, previous_pass.tr_count, tr_count); } ID3D11ShaderResourceView *p = nullptr; deviceContext->PSSetShaderResources(4, 1, &p); if (!theDX11Context.isIntel()) { // Intel Iris Plus 640 just crashes if (current_pass.mv_op_tr_shared) drawModVols(previous_pass.mvo_count, mvo_count, pvrrc.global_param_mvo.head()); else drawModVols(previous_pass.mvo_tr_count, current_pass.mvo_tr_count - previous_pass.mvo_tr_count, pvrrc.global_param_mvo_tr.head()); } } else { ID3D11ShaderResourceView *p = nullptr; deviceContext->PSSetShaderResources(4, 1, &p); drawList(pvrrc.global_param_tr, previous_pass.tr_count, tr_count); } if (render_pass < render_pass_count - 1) { // // PASS 3c: Render a-buffer to temporary texture // if (!multipassTex) { createTexAndRenderTarget(multipassTex, multipassRenderTarget, width, height); multipassTextureView.reset(); D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{}; viewDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; viewDesc.Texture2D.MipLevels = 1; device->CreateShaderResourceView(multipassTex, &viewDesc, &multipassTextureView.get()); } renderABuffer(false); std::swap(opaqueTex, multipassTex); std::swap(opaqueRenderTarget, multipassRenderTarget); std::swap(opaqueTextureView, multipassTextureView); deviceContext->PSSetShaderResources(0, 1, &p); deviceContext->IASetInputLayout(mainInputLayout); // Clear the stencil from this pass deviceContext->ClearDepthStencilView(depthStencilView2, D3D11_CLEAR_STENCIL, 0.f, 0); } previous_pass = current_pass; } // // PASS 4: Render a-buffers to screen // renderABuffer(true); } bool Render() override { resize(pvrrc.framebufferWidth, pvrrc.framebufferHeight); if (pixelBufferSize != config::PixelBufferSize) { buffers.init(device, deviceContext); pixelBufferSize = config::PixelBufferSize; } // Make sure to unbind the framebuffer view before setting it as render target ID3D11ShaderResourceView *p = nullptr; deviceContext->PSSetShaderResources(0, 1, &p); // To avoid DEVICE_DRAW_RENDERTARGETVIEW_NOT_SET warnings deviceContext->OMSetRenderTargets(1, &fbRenderTarget.get(), nullptr); configVertexShader(); bool is_rtt = pvrrc.isRTT; deviceContext->IASetInputLayout(mainInputLayout); n2Helper.resetCache(); uploadGeometryBuffers(); updateFogTexture(); updatePaletteTexture(); setupPixelShaderConstants(); drawStrips(); if (is_rtt) { readRttRenderTarget(pvrrc.fb_W_SOF1 & VRAM_MASK); } else if (config::EmulateFramebuffer) { writeFramebufferToVRAM(); } else { aspectRatio = getOutputFramebufferAspectRatio(); #ifndef LIBRETRO deviceContext->OMSetRenderTargets(1, &theDX11Context.getRenderTarget().get(), nullptr); displayFramebuffer(); DrawOSD(false); theDX11Context.setFrameRendered(); #else theDX11Context.drawOverlay(width, height); ID3D11RenderTargetView *nullView = nullptr; deviceContext->OMSetRenderTargets(1, &nullView, nullptr); deviceContext->PSSetShaderResources(0, 1, &fbTextureView.get()); #endif frameRendered = true; frameRenderedOnce = true; } return !is_rtt; } private: Buffers buffers; DX11OITShaders shaders; ComPtr opaqueTex; ComPtr opaqueRenderTarget; ComPtr opaqueTextureView; ComPtr multipassTex; ComPtr multipassRenderTarget; ComPtr multipassTextureView; ComPtr stencilView; ComPtr depthView; ComPtr depthStencilTex2; ComPtr depthStencilView2; ComPtr trPolyParamsBuffer; u32 trPolyParamsBufferSize = 0; ComPtr trPolyParamsBufferView; ComPtr mainInputLayout; // FIXME ComPtr finalInputLayout; ComPtr vtxPolyConstants; int64_t pixelBufferSize = 0; }; Renderer *rend_OITDirectX11() { return new DX11OITRenderer(); }