/*
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();
}