mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-12-12 10:37:02 +00:00
- GX2 utilizes TCL(.rpl) API for command submission instead of directly writing to an internal GPU fifo - Submission & retire timestamps are correctly implemented as incremental counters - Command buffering behaviour matches console - Fixes race conditions on aarch64
725 lines
28 KiB
C++
725 lines
28 KiB
C++
#include "Common/precompiled.h"
|
|
#include "GX2_State.h"
|
|
#include "GX2_Command.h"
|
|
#include "Cafe/HW/Latte/ISA/LatteReg.h"
|
|
#include "Cafe/HW/Latte/Core/LattePM4.h"
|
|
#include "Cafe/OS/common/OSCommon.h"
|
|
|
|
namespace GX2
|
|
{
|
|
using namespace Latte;
|
|
|
|
void GX2InitAlphaTestReg(GX2AlphaTestReg* reg, uint32 alphaTestEnable, GX2_ALPHAFUNC alphaFunc, float alphaRef)
|
|
{
|
|
Latte::LATTE_SX_ALPHA_TEST_CONTROL tmpRegCtrl;
|
|
tmpRegCtrl.set_ALPHA_FUNC(alphaFunc);
|
|
tmpRegCtrl.set_ALPHA_TEST_ENABLE(alphaTestEnable != 0);
|
|
reg->regAlphaTestControl = tmpRegCtrl;
|
|
|
|
Latte::LATTE_SX_ALPHA_REF tmpRegRef;
|
|
tmpRegRef.set_ALPHA_TEST_REF(alphaRef);
|
|
reg->regAlphaTestRef = tmpRegRef;
|
|
}
|
|
|
|
void GX2SetAlphaTestReg(GX2AlphaTestReg* reg)
|
|
{
|
|
GX2ReserveCmdSpace(3 + 3);
|
|
gx2WriteGather_submit(
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::SX_ALPHA_TEST_CONTROL - 0xA000,
|
|
reg->regAlphaTestControl,
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::SX_ALPHA_REF - 0xA000,
|
|
reg->regAlphaTestRef);
|
|
}
|
|
|
|
void GX2SetAlphaTest(uint32 alphaTestEnable, GX2_ALPHAFUNC alphaFunc, float alphaRef)
|
|
{
|
|
GX2AlphaTestReg tmpReg;
|
|
GX2InitAlphaTestReg(&tmpReg, alphaTestEnable, alphaFunc, alphaRef);
|
|
GX2SetAlphaTestReg(&tmpReg);
|
|
}
|
|
|
|
void GX2InitColorControlReg(GX2ColorControlReg* reg, GX2_LOGICOP logicOp, uint32 blendMask, uint32 multiwriteEnable, uint32 colorBufferEnable)
|
|
{
|
|
Latte::LATTE_CB_COLOR_CONTROL colorControlReg2;
|
|
colorControlReg2.set_MULTIWRITE_ENABLE(multiwriteEnable != 0);
|
|
if (colorBufferEnable == 0)
|
|
colorControlReg2.set_SPECIAL_OP(Latte::LATTE_CB_COLOR_CONTROL::E_SPECIALOP::DISABLE);
|
|
else
|
|
colorControlReg2.set_SPECIAL_OP(Latte::LATTE_CB_COLOR_CONTROL::E_SPECIALOP::NORMAL);
|
|
colorControlReg2.set_BLEND_MASK(blendMask);
|
|
colorControlReg2.set_ROP(logicOp);
|
|
reg->reg = colorControlReg2;
|
|
}
|
|
|
|
void GX2SetColorControlReg(GX2ColorControlReg* reg)
|
|
{
|
|
GX2ReserveCmdSpace(3);
|
|
gx2WriteGather_submit(
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::CB_COLOR_CONTROL - 0xA000,
|
|
reg->reg);
|
|
}
|
|
|
|
void GX2SetColorControl(GX2_LOGICOP logicOp, uint32 blendMask, uint32 multiwriteEnable, uint32 colorBufferEnable)
|
|
{
|
|
GX2ColorControlReg colorControlReg;
|
|
GX2InitColorControlReg(&colorControlReg, logicOp, blendMask, multiwriteEnable, colorBufferEnable);
|
|
GX2SetColorControlReg(&colorControlReg);
|
|
}
|
|
|
|
|
|
void GX2InitPolygonControlReg(GX2PolygonControlReg* reg,
|
|
Latte::LATTE_PA_SU_SC_MODE_CNTL::E_FRONTFACE frontFace,
|
|
uint32 cullFront,
|
|
uint32 cullBack,
|
|
Latte::LATTE_PA_SU_SC_MODE_CNTL::E_POLYGONMODE usePolygonMode,
|
|
Latte::LATTE_PA_SU_SC_MODE_CNTL::E_PTYPE polyModeFront,
|
|
Latte::LATTE_PA_SU_SC_MODE_CNTL::E_PTYPE polyModeBack,
|
|
uint32 polygonOffsetFrontEnable,
|
|
uint32 polygonOffsetBackEnable,
|
|
uint32 paraOffsetEnable)
|
|
{
|
|
Latte::LATTE_PA_SU_SC_MODE_CNTL v;
|
|
v.set_FRONT_FACE(frontFace);
|
|
v.set_CULL_FRONT((cullFront & 1) != 0);
|
|
v.set_CULL_BACK((cullBack & 1) != 0);
|
|
v.set_POLYGON_MODE(usePolygonMode);
|
|
v.set_FRONT_POLY_MODE(polyModeFront);
|
|
v.set_BACK_POLY_MODE(polyModeBack);
|
|
v.set_OFFSET_PARA_ENABLED((paraOffsetEnable & 1) != 0);
|
|
v.set_OFFSET_FRONT_ENABLED((polygonOffsetFrontEnable & 1) != 0);
|
|
v.set_OFFSET_BACK_ENABLED((polygonOffsetBackEnable & 1) != 0);
|
|
reg->reg = v;
|
|
}
|
|
|
|
void GX2SetPolygonControlReg(GX2PolygonControlReg* reg)
|
|
{
|
|
GX2ReserveCmdSpace(3);
|
|
gx2WriteGather_submit(
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::PA_SU_SC_MODE_CNTL - 0xA000,
|
|
reg->reg);
|
|
}
|
|
|
|
void GX2SetPolygonControl(Latte::LATTE_PA_SU_SC_MODE_CNTL::E_FRONTFACE frontFace,
|
|
uint32 cullFront,
|
|
uint32 cullBack,
|
|
Latte::LATTE_PA_SU_SC_MODE_CNTL::E_POLYGONMODE usePolygonMode,
|
|
Latte::LATTE_PA_SU_SC_MODE_CNTL::E_PTYPE polyModeFront,
|
|
Latte::LATTE_PA_SU_SC_MODE_CNTL::E_PTYPE polyModeBack,
|
|
uint32 polygonOffsetFrontEnable,
|
|
uint32 polygonOffsetBackEnable,
|
|
uint32 paraOffsetEnable)
|
|
{
|
|
GX2PolygonControlReg reg{};
|
|
GX2InitPolygonControlReg(®, frontFace, cullFront, cullBack, usePolygonMode, polyModeFront, polyModeBack, polygonOffsetFrontEnable, polygonOffsetBackEnable, paraOffsetEnable);
|
|
GX2SetPolygonControlReg(®);
|
|
}
|
|
|
|
void GX2SetCullOnlyControl(Latte::LATTE_PA_SU_SC_MODE_CNTL::E_FRONTFACE frontFace, uint32 cullFront, uint32 cullBack)
|
|
{
|
|
GX2PolygonControlReg reg{};
|
|
GX2InitPolygonControlReg(®, frontFace, cullFront, cullBack, Latte::LATTE_PA_SU_SC_MODE_CNTL::E_POLYGONMODE::UKN0, Latte::LATTE_PA_SU_SC_MODE_CNTL::E_PTYPE::POINTS, Latte::LATTE_PA_SU_SC_MODE_CNTL::E_PTYPE::POINTS, 0, 0, 0);
|
|
GX2SetPolygonControlReg(®);
|
|
}
|
|
|
|
void GX2InitPolygonOffsetReg(GX2PolygonOffsetReg* reg, float frontOffset, float frontScale, float backOffset, float backScale, float clampOffset)
|
|
{
|
|
frontScale *= 16.0;
|
|
backScale *= 16.0;
|
|
reg->regFrontScale = Latte::LATTE_PA_SU_POLY_OFFSET_FRONT_SCALE().set_SCALE(frontScale);
|
|
reg->regFrontOffset = Latte::LATTE_PA_SU_POLY_OFFSET_FRONT_OFFSET().set_OFFSET(frontOffset);
|
|
reg->regBackScale = Latte::LATTE_PA_SU_POLY_OFFSET_BACK_SCALE().set_SCALE(backScale);
|
|
reg->regBackOffset = Latte::LATTE_PA_SU_POLY_OFFSET_BACK_OFFSET().set_OFFSET(backOffset);
|
|
reg->regClamp = Latte::LATTE_PA_SU_POLY_OFFSET_CLAMP().set_CLAMP(clampOffset);
|
|
}
|
|
|
|
void GX2SetPolygonOffsetReg(GX2PolygonOffsetReg* reg)
|
|
{
|
|
GX2ReserveCmdSpace(6 + 3);
|
|
|
|
gx2WriteGather_submit(
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 4),
|
|
Latte::REGADDR::PA_SU_POLY_OFFSET_FRONT_SCALE - 0xA000,
|
|
reg->regFrontScale,
|
|
reg->regFrontOffset,
|
|
reg->regBackScale,
|
|
reg->regBackOffset,
|
|
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::PA_SU_POLY_OFFSET_CLAMP - 0xA000,
|
|
reg->regClamp);
|
|
}
|
|
|
|
void GX2SetPolygonOffset(float frontOffset, float frontScale, float backOffset, float backScale, float clampOffset)
|
|
{
|
|
GX2PolygonOffsetReg tmpReg;
|
|
GX2InitPolygonOffsetReg(&tmpReg, frontOffset, frontScale, backOffset, backScale, clampOffset);
|
|
GX2SetPolygonOffsetReg(&tmpReg);
|
|
}
|
|
|
|
void GX2SetRasterizerClipControlEx(bool enableRasterizer, bool enableZClip, bool enableHalfZ)
|
|
{
|
|
GX2ReserveCmdSpace(3);
|
|
|
|
//if (enableHalfZ)
|
|
//{
|
|
// // Smash has a bug where it enables half space clipping during streamout drawcalls and shadowing and then doesn't turn it off until the next GX2SetRasterizerClipControl call
|
|
// // this leads to some stuff being rendered at the wrong z-plane (e.g. shields behind characters) if the game's default depth range -1 to 1 isn't supported (on OpenGL only Nvidia's glDepthRangedNV allows unclamped values)
|
|
// uint64 titleId = gameMeta_getTitleId();
|
|
// if (titleId == 0x0005000010144F00ULL ||
|
|
// titleId == 0x0005000010145000ULL ||
|
|
// titleId == 0x0005000010110E00ULL)
|
|
// {
|
|
// // force disable half space clipping
|
|
// if (g_renderer && g_renderer->GetType() == RendererAPI::OpenGL && LatteGPUState.glVendor != GLVENDOR_NVIDIA)
|
|
// enableHalfZ = false;
|
|
// }
|
|
//}
|
|
|
|
Latte::LATTE_PA_CL_CLIP_CNTL reg{};
|
|
reg.set_ZCLIP_NEAR_DISABLE(!enableZClip).set_ZCLIP_FAR_DISABLE(!enableZClip);
|
|
reg.set_DX_RASTERIZATION_KILL(!enableRasterizer);
|
|
reg.set_DX_CLIP_SPACE_DEF(enableHalfZ);
|
|
reg.set_DX_LINEAR_ATTR_CLIP_ENA(true);
|
|
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::PA_CL_CLIP_CNTL - 0xA000,
|
|
reg);
|
|
}
|
|
|
|
void GX2SetRasterizerClipControl(bool enableRasterizer, bool enableZClip)
|
|
{
|
|
GX2SetRasterizerClipControlEx(enableRasterizer, enableZClip, false);
|
|
}
|
|
|
|
void GX2SetRasterizerClipControlHalfZ(bool enableRasterizer, bool enableZClip, bool enableHalfZ)
|
|
{
|
|
GX2SetRasterizerClipControlEx(enableRasterizer, enableZClip, enableHalfZ);
|
|
}
|
|
|
|
void GX2InitViewportReg(GX2ViewportReg* viewportReg, float x, float y, float width, float height, float nearZ, float farZ)
|
|
{
|
|
// todo: set clipping registers and zMin/zMax registers
|
|
viewportReg->xScale = Latte::LATTE_PA_CL_VPORT_XSCALE().set_SCALE(width * 0.5f);
|
|
viewportReg->xOffset = Latte::LATTE_PA_CL_VPORT_XOFFSET().set_OFFSET(x + (width * 0.5f));
|
|
viewportReg->yScale = Latte::LATTE_PA_CL_VPORT_YSCALE().set_SCALE(height * -0.5f);
|
|
viewportReg->yOffset = Latte::LATTE_PA_CL_VPORT_YOFFSET().set_OFFSET(y + (height * 0.5f));
|
|
viewportReg->zScale = Latte::LATTE_PA_CL_VPORT_ZSCALE().set_SCALE((farZ - nearZ) * 0.5f);
|
|
viewportReg->zOffset = Latte::LATTE_PA_CL_VPORT_ZOFFSET().set_OFFSET((nearZ + farZ) * 0.5f);
|
|
}
|
|
|
|
void GX2SetViewportReg(GX2ViewportReg* viewportReg)
|
|
{
|
|
GX2ReserveCmdSpace(2 + 6);
|
|
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 6),
|
|
Latte::REGADDR::PA_CL_VPORT_XSCALE - 0xA000,
|
|
viewportReg->xScale, viewportReg->xOffset,
|
|
viewportReg->yScale, viewportReg->yOffset,
|
|
viewportReg->zScale, viewportReg->zOffset);
|
|
}
|
|
|
|
void GX2SetViewport(float x, float y, float width, float height, float nearZ, float farZ)
|
|
{
|
|
GX2ViewportReg viewportReg;
|
|
GX2InitViewportReg(&viewportReg, x, y, width, height, nearZ, farZ);
|
|
GX2SetViewportReg(&viewportReg);
|
|
}
|
|
|
|
void GX2InitScissorReg(GX2ScissorReg* scissorReg, uint32 x, uint32 y, uint32 width, uint32 height)
|
|
{
|
|
uint32 tlx = x;
|
|
uint32 tly = y;
|
|
uint32 brx = x + width;
|
|
uint32 bry = y + height;
|
|
|
|
tlx = std::min(tlx, 8192u);
|
|
tly = std::min(tly, 8192u);
|
|
brx = std::min(brx, 8192u);
|
|
bry = std::min(bry, 8192u);
|
|
|
|
scissorReg->scissorTL = Latte::LATTE_PA_SC_GENERIC_SCISSOR_TL().set_TL_X(tlx).set_TL_Y(tly).set_WINDOW_OFFSET_DISABLE(true);
|
|
scissorReg->scissorBR = Latte::LATTE_PA_SC_GENERIC_SCISSOR_BR().set_BR_X(brx).set_BR_Y(bry);
|
|
}
|
|
|
|
void GX2SetScissorReg(GX2ScissorReg* scissorReg)
|
|
{
|
|
GX2ReserveCmdSpace(4);
|
|
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 2),
|
|
Latte::REGADDR::PA_SC_GENERIC_SCISSOR_TL - 0xA000,
|
|
scissorReg->scissorTL, scissorReg->scissorBR);
|
|
}
|
|
|
|
void GX2GetScissorReg(GX2ScissorReg* scissorReg, uint32be* x, uint32be* y, uint32be* width, uint32be* height)
|
|
{
|
|
*x = scissorReg->scissorTL.value().get_TL_X();
|
|
*y = scissorReg->scissorTL.value().get_TL_Y();
|
|
*width = scissorReg->scissorBR.value().get_BR_X() - scissorReg->scissorTL.value().get_TL_X();
|
|
*height = scissorReg->scissorBR.value().get_BR_Y() - scissorReg->scissorTL.value().get_TL_Y();
|
|
}
|
|
|
|
void GX2SetScissor(uint32 x, uint32 y, uint32 width, uint32 height)
|
|
{
|
|
GX2ScissorReg scissorReg;
|
|
GX2InitScissorReg(&scissorReg, x, y, width, height);
|
|
GX2SetScissorReg(&scissorReg);
|
|
}
|
|
|
|
void GX2SetDepthOnlyControl(bool depthTestEnable, bool depthWriteEnable, LATTE_DB_DEPTH_CONTROL::E_ZFUNC depthFunction)
|
|
{
|
|
// disables any currently set stencil test
|
|
GX2ReserveCmdSpace(3);
|
|
|
|
Latte::LATTE_DB_DEPTH_CONTROL reg{};
|
|
reg.set_Z_ENABLE(depthTestEnable);
|
|
reg.set_Z_WRITE_ENABLE(depthWriteEnable);
|
|
reg.set_Z_FUNC(depthFunction);
|
|
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::DB_DEPTH_CONTROL - 0xA000,
|
|
reg);
|
|
}
|
|
|
|
void GX2SetDepthStencilControl(
|
|
bool depthTestEnable, bool depthWriteEnable, LATTE_DB_DEPTH_CONTROL::E_ZFUNC depthFunction,
|
|
bool stencilTestEnable, bool backStencilTestEnable,
|
|
LATTE_DB_DEPTH_CONTROL::E_STENCILFUNC frontStencilFunction,
|
|
LATTE_DB_DEPTH_CONTROL::E_STENCILACTION frontStencilZPass, LATTE_DB_DEPTH_CONTROL::E_STENCILACTION frontStencilZFail, LATTE_DB_DEPTH_CONTROL::E_STENCILACTION frontStencilFail,
|
|
LATTE_DB_DEPTH_CONTROL::E_STENCILFUNC backStencilFunction,
|
|
LATTE_DB_DEPTH_CONTROL::E_STENCILACTION backStencilZPass, LATTE_DB_DEPTH_CONTROL::E_STENCILACTION backStencilZFail, LATTE_DB_DEPTH_CONTROL::E_STENCILACTION backStencilFail
|
|
)
|
|
{
|
|
GX2ReserveCmdSpace(3);
|
|
|
|
Latte::LATTE_DB_DEPTH_CONTROL reg{};
|
|
reg.set_Z_ENABLE(depthTestEnable).set_Z_WRITE_ENABLE(depthWriteEnable).set_Z_FUNC(depthFunction);
|
|
reg.set_STENCIL_ENABLE(stencilTestEnable).set_BACK_STENCIL_ENABLE(backStencilTestEnable);
|
|
reg.set_STENCIL_FUNC_F(frontStencilFunction).set_STENCIL_FUNC_B(backStencilFunction);
|
|
reg.set_STENCIL_ZPASS_F(frontStencilZPass).set_STENCIL_ZFAIL_F(frontStencilZFail).set_STENCIL_FAIL_F(frontStencilFail);
|
|
reg.set_STENCIL_ZPASS_B(backStencilZPass).set_STENCIL_ZFAIL_B(backStencilZFail).set_STENCIL_FAIL_B(backStencilFail);
|
|
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::DB_DEPTH_CONTROL - 0xA000,
|
|
reg);
|
|
}
|
|
|
|
void GX2InitDepthStencilControlReg(
|
|
GX2DepthStencilControlReg* depthStencilControlReg,
|
|
bool depthTestEnable, bool depthWriteEnable, LATTE_DB_DEPTH_CONTROL::E_ZFUNC depthFunction,
|
|
bool stencilTestEnable, bool backStencilTestEnable,
|
|
LATTE_DB_DEPTH_CONTROL::E_STENCILFUNC frontStencilFunction,
|
|
LATTE_DB_DEPTH_CONTROL::E_STENCILACTION frontStencilZPass, LATTE_DB_DEPTH_CONTROL::E_STENCILACTION frontStencilZFail, LATTE_DB_DEPTH_CONTROL::E_STENCILACTION frontStencilFail,
|
|
LATTE_DB_DEPTH_CONTROL::E_STENCILFUNC backStencilFunction,
|
|
LATTE_DB_DEPTH_CONTROL::E_STENCILACTION backStencilZPass, LATTE_DB_DEPTH_CONTROL::E_STENCILACTION backStencilZFail, LATTE_DB_DEPTH_CONTROL::E_STENCILACTION backStencilFail)
|
|
{
|
|
Latte::LATTE_DB_DEPTH_CONTROL reg{};
|
|
reg.set_Z_ENABLE(depthTestEnable).set_Z_WRITE_ENABLE(depthWriteEnable).set_Z_FUNC(depthFunction);
|
|
reg.set_STENCIL_ENABLE(stencilTestEnable).set_BACK_STENCIL_ENABLE(backStencilTestEnable);
|
|
reg.set_STENCIL_FUNC_F(frontStencilFunction).set_STENCIL_FUNC_B(backStencilFunction);
|
|
reg.set_STENCIL_ZPASS_F(frontStencilZPass).set_STENCIL_ZFAIL_F(frontStencilZFail).set_STENCIL_FAIL_F(frontStencilFail);
|
|
reg.set_STENCIL_ZPASS_B(backStencilZPass).set_STENCIL_ZFAIL_B(backStencilZFail).set_STENCIL_FAIL_B(backStencilFail);
|
|
depthStencilControlReg->reg = reg;
|
|
}
|
|
|
|
void GX2SetDepthStencilControlReg(GX2DepthStencilControlReg* depthStencilControlReg)
|
|
{
|
|
GX2ReserveCmdSpace(3);
|
|
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::DB_DEPTH_CONTROL - 0xA000,
|
|
depthStencilControlReg->reg);
|
|
}
|
|
|
|
void GX2GetDepthStencilControlReg(
|
|
GX2DepthStencilControlReg* depthStencilControlReg,
|
|
uint32be* depthTestEnable, uint32be* depthWriteEnable, uint32be* depthFunction,
|
|
uint32be* stencilTestEnable, uint32be* backStencilTestEnable,
|
|
uint32be* frontStencilFunction,
|
|
uint32be* frontStencilZPass, uint32be* frontStencilZFail, uint32be* frontStencilFail,
|
|
uint32be* backStencilFunction,
|
|
uint32be* backStencilZPass, uint32be* backStencilZFail, uint32be* backStencilFail)
|
|
{
|
|
// used by Hyrule Warriors
|
|
|
|
*depthTestEnable = depthStencilControlReg->reg.value().get_Z_ENABLE();
|
|
|
|
*depthWriteEnable = depthStencilControlReg->reg.value().get_Z_WRITE_ENABLE();
|
|
*depthFunction = (uint32)depthStencilControlReg->reg.value().get_Z_FUNC();
|
|
|
|
*stencilTestEnable = depthStencilControlReg->reg.value().get_STENCIL_ENABLE();
|
|
*backStencilTestEnable = depthStencilControlReg->reg.value().get_BACK_STENCIL_ENABLE();
|
|
|
|
*frontStencilFunction = (uint32)depthStencilControlReg->reg.value().get_STENCIL_FUNC_F();
|
|
*backStencilFunction = (uint32)depthStencilControlReg->reg.value().get_STENCIL_FUNC_B();
|
|
|
|
*frontStencilZPass = (uint32)depthStencilControlReg->reg.value().get_STENCIL_ZPASS_F();
|
|
*frontStencilZFail = (uint32)depthStencilControlReg->reg.value().get_STENCIL_ZFAIL_F();
|
|
*frontStencilFail = (uint32)depthStencilControlReg->reg.value().get_STENCIL_FAIL_F();
|
|
|
|
*backStencilZPass = (uint32)depthStencilControlReg->reg.value().get_STENCIL_ZPASS_B();
|
|
*backStencilZFail = (uint32)depthStencilControlReg->reg.value().get_STENCIL_ZFAIL_B();
|
|
*backStencilFail = (uint32)depthStencilControlReg->reg.value().get_STENCIL_FAIL_B();
|
|
}
|
|
|
|
void GX2InitStencilMaskReg(GX2StencilMaskReg* stencilMaskReg, uint8 compareMaskFront, uint8 writeMaskFront, uint8 refFront, uint8 compareMaskBack, uint8 writeMaskBack, uint8 refBack)
|
|
{
|
|
stencilMaskReg->stencilRefMaskFrontReg = LATTE_DB_STENCILREFMASK().set_STENCILREF_F(refFront).set_STENCILMASK_F(compareMaskFront).set_STENCILWRITEMASK_F(writeMaskFront);
|
|
stencilMaskReg->stencilRefMaskBackReg = LATTE_DB_STENCILREFMASK_BF().set_STENCILREF_B(refBack).set_STENCILMASK_B(compareMaskBack).set_STENCILWRITEMASK_B(writeMaskBack);
|
|
}
|
|
|
|
void GX2SetStencilMask(uint8 compareMaskFront, uint8 writeMaskFront, uint8 refFront, uint8 compareMaskBack, uint8 writeMaskBack, uint8 refBack)
|
|
{
|
|
GX2ReserveCmdSpace(3 + 3);
|
|
|
|
LATTE_DB_STENCILREFMASK frontReg;
|
|
frontReg.set_STENCILREF_F(refFront).set_STENCILMASK_F(compareMaskFront).set_STENCILWRITEMASK_F(writeMaskFront);
|
|
LATTE_DB_STENCILREFMASK_BF backReg;
|
|
backReg.set_STENCILREF_B(refBack).set_STENCILMASK_B(compareMaskBack).set_STENCILWRITEMASK_B(writeMaskBack);
|
|
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
REGADDR::DB_STENCILREFMASK - 0xA000,
|
|
frontReg,
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
REGADDR::DB_STENCILREFMASK_BF - 0xA000,
|
|
backReg);
|
|
}
|
|
|
|
void GX2SetStencilMaskReg(GX2StencilMaskReg* stencilMaskReg)
|
|
{
|
|
GX2ReserveCmdSpace(3 + 3);
|
|
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
REGADDR::DB_STENCILREFMASK - 0xA000,
|
|
stencilMaskReg->stencilRefMaskFrontReg,
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
REGADDR::DB_STENCILREFMASK_BF - 0xA000,
|
|
stencilMaskReg->stencilRefMaskBackReg);
|
|
}
|
|
|
|
void GX2SetPrimitiveRestartIndex(uint32 restartIndex)
|
|
{
|
|
GX2ReserveCmdSpace(3);
|
|
Latte::LATTE_VGT_MULTI_PRIM_IB_RESET_INDX reg{};
|
|
reg.set_RESTART_INDEX(restartIndex);
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::VGT_MULTI_PRIM_IB_RESET_INDX - 0xA000,
|
|
reg);
|
|
}
|
|
|
|
void GX2InitTargetChannelMasksReg(GX2TargetChannelMaskReg* reg, GX2_CHANNELMASK t0, GX2_CHANNELMASK t1, GX2_CHANNELMASK t2, GX2_CHANNELMASK t3, GX2_CHANNELMASK t4, GX2_CHANNELMASK t5, GX2_CHANNELMASK t6, GX2_CHANNELMASK t7)
|
|
{
|
|
uint32 targetMask = 0;
|
|
targetMask |= ((t0 & 0xF) << 0);
|
|
targetMask |= ((t1 & 0xF) << 4);
|
|
targetMask |= ((t2 & 0xF) << 8);
|
|
targetMask |= ((t3 & 0xF) << 12);
|
|
targetMask |= ((t4 & 0xF) << 16);
|
|
targetMask |= ((t5 & 0xF) << 20);
|
|
targetMask |= ((t6 & 0xF) << 24);
|
|
targetMask |= ((t7 & 0xF) << 28);
|
|
Latte::LATTE_CB_TARGET_MASK r;
|
|
r.set_MASK(targetMask);
|
|
reg->reg = r;
|
|
}
|
|
|
|
void GX2SetTargetChannelMasksReg(GX2TargetChannelMaskReg* reg)
|
|
{
|
|
GX2ReserveCmdSpace(3);
|
|
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::CB_TARGET_MASK - 0xA000,
|
|
reg->reg);
|
|
}
|
|
|
|
void GX2SetTargetChannelMasks(GX2_CHANNELMASK t0, GX2_CHANNELMASK t1, GX2_CHANNELMASK t2, GX2_CHANNELMASK t3, GX2_CHANNELMASK t4, GX2_CHANNELMASK t5, GX2_CHANNELMASK t6, GX2_CHANNELMASK t7)
|
|
{
|
|
GX2TargetChannelMaskReg tmpReg;
|
|
GX2InitTargetChannelMasksReg(&tmpReg, t0, t1, t2, t3, t4, t5, t6, t7);
|
|
GX2SetTargetChannelMasksReg(&tmpReg);
|
|
}
|
|
|
|
static_assert(sizeof(GX2_CHANNELMASK) == 4);
|
|
|
|
void GX2GetTargetChannelMasksReg(GX2TargetChannelMaskReg* reg, betype<GX2_CHANNELMASK>* t0, betype<GX2_CHANNELMASK>* t1, betype<GX2_CHANNELMASK>* t2, betype<GX2_CHANNELMASK>* t3,
|
|
betype<GX2_CHANNELMASK>* t4, betype<GX2_CHANNELMASK>* t5, betype<GX2_CHANNELMASK>* t6, betype<GX2_CHANNELMASK>* t7)
|
|
{
|
|
uint32 maskValue = reg->reg.value().get_MASK();
|
|
*t0 = (maskValue >> 0) & 0xF;
|
|
*t1 = (maskValue >> 4) & 0xF;
|
|
*t2 = (maskValue >> 8) & 0xF;
|
|
*t3 = (maskValue >> 12) & 0xF;
|
|
*t4 = (maskValue >> 16) & 0xF;
|
|
*t5 = (maskValue >> 20) & 0xF;
|
|
*t6 = (maskValue >> 24) & 0xF;
|
|
*t7 = (maskValue >> 28) & 0xF;
|
|
}
|
|
|
|
void GX2InitBlendControlReg(GX2BlendControlReg* reg, uint32 renderTargetIndex, GX2_BLENDFACTOR colorSrcFactor, GX2_BLENDFACTOR colorDstFactor, GX2_BLENDFUNC colorCombineFunc, uint32 separateAlphaBlend, GX2_BLENDFACTOR alphaSrcFactor, GX2_BLENDFACTOR alphaDstFactor, GX2_BLENDFUNC alphaCombineFunc)
|
|
{
|
|
Latte::LATTE_CB_BLENDN_CONTROL tmpReg;
|
|
tmpReg.set_COLOR_SRCBLEND(colorSrcFactor);
|
|
tmpReg.set_COLOR_DSTBLEND(colorDstFactor);
|
|
tmpReg.set_COLOR_COMB_FCN(colorCombineFunc);
|
|
tmpReg.set_ALPHA_SRCBLEND(alphaSrcFactor);
|
|
tmpReg.set_ALPHA_DSTBLEND(alphaDstFactor);
|
|
tmpReg.set_ALPHA_COMB_FCN(alphaCombineFunc);
|
|
tmpReg.set_SEPARATE_ALPHA_BLEND(separateAlphaBlend != 0);
|
|
|
|
reg->index = renderTargetIndex;
|
|
reg->reg = tmpReg;
|
|
}
|
|
|
|
void GX2SetBlendControlReg(GX2BlendControlReg* reg)
|
|
{
|
|
GX2ReserveCmdSpace(3);
|
|
gx2WriteGather_submit(
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
(Latte::REGADDR::CB_BLEND0_CONTROL + (uint32)reg->index) - 0xA000,
|
|
reg->reg
|
|
);
|
|
}
|
|
|
|
void GX2SetBlendControl(uint32 renderTargetIndex, GX2_BLENDFACTOR colorSrcFactor, GX2_BLENDFACTOR colorDstFactor, GX2_BLENDFUNC colorCombineFunc, uint32 separateAlphaBlend, GX2_BLENDFACTOR alphaSrcFactor, GX2_BLENDFACTOR alphaDstFactor, GX2_BLENDFUNC alphaCombineFunc)
|
|
{
|
|
GX2BlendControlReg tmpReg;
|
|
GX2InitBlendControlReg(&tmpReg, renderTargetIndex, colorSrcFactor, colorDstFactor, colorCombineFunc, separateAlphaBlend, alphaSrcFactor, alphaDstFactor, alphaCombineFunc);
|
|
GX2SetBlendControlReg(&tmpReg);
|
|
}
|
|
|
|
void GX2InitBlendConstantColorReg(GX2BlendConstantColorReg* reg, float red, float green, float blue, float alpha)
|
|
{
|
|
reg->regRed = Latte::LATTE_CB_BLEND_RED().set_RED(red);
|
|
reg->regGreen = Latte::LATTE_CB_BLEND_GREEN().set_GREEN(green);
|
|
reg->regBlue = Latte::LATTE_CB_BLEND_BLUE().set_BLUE(blue);
|
|
reg->regAlpha = Latte::LATTE_CB_BLEND_ALPHA().set_ALPHA(alpha);
|
|
}
|
|
|
|
void GX2SetBlendConstantColorReg(GX2BlendConstantColorReg* reg)
|
|
{
|
|
GX2ReserveCmdSpace(6);
|
|
gx2WriteGather_submit(
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 4),
|
|
Latte::REGADDR::CB_BLEND_RED - 0xA000,
|
|
reg->regRed,
|
|
reg->regGreen,
|
|
reg->regBlue,
|
|
reg->regAlpha
|
|
);
|
|
}
|
|
|
|
void GX2SetBlendConstantColor(float red, float green, float blue, float alpha)
|
|
{
|
|
GX2BlendConstantColorReg tmpReg;
|
|
GX2InitBlendConstantColorReg(&tmpReg, red, green, blue, alpha);
|
|
GX2SetBlendConstantColorReg(&tmpReg);
|
|
}
|
|
|
|
void GX2InitHiStencilInfoRegs(GX2HiStencilInfoReg* hiStencilInfo)
|
|
{
|
|
// seen in Color Splash
|
|
// but the game never calls GX2SetHiStencilInfo thus this has no effect
|
|
}
|
|
|
|
void GX2InitPointSizeReg(GX2PointSizeReg* reg, float width, float height)
|
|
{
|
|
if (width < 0.0f || height < 0.0f)
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
|
|
uint32 widthI = (uint32)(width * 8.0f);
|
|
uint32 heightI = (uint32)(height * 8.0f);
|
|
|
|
widthI = std::min<uint32>(widthI, 0xFFFF);
|
|
heightI = std::min<uint32>(heightI, 0xFFFF);
|
|
|
|
reg->reg = Latte::LATTE_PA_SU_POINT_SIZE().set_WIDTH(widthI).set_HEIGHT(heightI);
|
|
}
|
|
|
|
void GX2SetPointSizeReg(GX2PointSizeReg* reg)
|
|
{
|
|
GX2ReserveCmdSpace(3);
|
|
gx2WriteGather_submit(
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::PA_SU_POINT_SIZE - 0xA000,
|
|
reg->reg
|
|
);
|
|
}
|
|
|
|
void GX2SetPointSize(float width, float height)
|
|
{
|
|
GX2PointSizeReg tmpReg;
|
|
GX2InitPointSizeReg(&tmpReg, width, height);
|
|
GX2SetPointSizeReg(&tmpReg);
|
|
}
|
|
|
|
void GX2InitPointLimitsReg(GX2PointLimitsReg* reg, float minSize, float maxSize)
|
|
{
|
|
if (minSize < 0.0f || maxSize < 0.0f)
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
|
|
uint32 minSizeI = (uint32)(minSize * 8.0f);
|
|
uint32 maxSizeI = (uint32)(maxSize * 8.0f);
|
|
|
|
minSizeI = std::min<uint32>(minSizeI, 0xFFFF);
|
|
maxSizeI = std::min<uint32>(maxSizeI, 0xFFFF);
|
|
|
|
reg->reg = Latte::LATTE_PA_SU_POINT_MINMAX().set_MIN_SIZE(minSizeI).set_MAX_SIZE(maxSizeI);
|
|
}
|
|
|
|
void GX2SetPointLimitsReg(GX2PointLimitsReg* reg)
|
|
{
|
|
GX2ReserveCmdSpace(3);
|
|
gx2WriteGather_submit(
|
|
pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::PA_SU_POINT_MINMAX - 0xA000,
|
|
reg->reg
|
|
);
|
|
}
|
|
|
|
void GX2SetPointLimits(float minSize, float maxSize)
|
|
{
|
|
GX2PointLimitsReg tmpReg;
|
|
GX2InitPointLimitsReg(&tmpReg, minSize, maxSize);
|
|
GX2SetPointLimitsReg(&tmpReg);
|
|
}
|
|
|
|
enum class GX2_SPECIAL_STATE : uint32
|
|
{
|
|
FAST_CLEAR = 0,
|
|
FAST_CLEAR_HIZ = 1,
|
|
};
|
|
|
|
void _setSpecialState0(bool isEnabled)
|
|
{
|
|
GX2ReserveCmdSpace(6);
|
|
if (isEnabled)
|
|
{
|
|
// set PA_CL_VTE_CNTL to 0x300
|
|
Latte::LATTE_PA_CL_VTE_CNTL regVTE{};
|
|
regVTE.set_VTX_XY_FMT(true);
|
|
regVTE.set_VTX_Z_FMT(true);
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::PA_CL_VTE_CNTL - 0xA000,
|
|
regVTE);
|
|
// set PA_CL_CLIP_CNTL to 0x490000
|
|
Latte::LATTE_PA_CL_CLIP_CNTL regClip{};
|
|
regClip.set_CLIP_DISABLE(true); // 0x10000
|
|
regClip.set_DX_CLIP_SPACE_DEF(true); // 0x80000
|
|
regClip.set_DX_RASTERIZATION_KILL(true); // 0x400000
|
|
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::PA_CL_CLIP_CNTL - 0xA000,
|
|
regClip);
|
|
}
|
|
else
|
|
{
|
|
// set PA_CL_VTE_CNTL to 0x43F
|
|
Latte::LATTE_PA_CL_VTE_CNTL reg{};
|
|
reg.set_VPORT_X_OFFSET_ENA(true).set_VPORT_X_SCALE_ENA(true);
|
|
reg.set_VPORT_Y_OFFSET_ENA(true).set_VPORT_Y_SCALE_ENA(true);
|
|
reg.set_VPORT_Z_OFFSET_ENA(true).set_VPORT_Z_SCALE_ENA(true);
|
|
reg.set_VTX_W0_FMT(true);
|
|
gx2WriteGather_submit(pm4HeaderType3(IT_SET_CONTEXT_REG, 1 + 1),
|
|
Latte::REGADDR::PA_CL_VTE_CNTL - 0xA000,
|
|
reg);
|
|
// reset PA_CL_CLIP_CNTL
|
|
GX2SetRasterizerClipControl(true, true);
|
|
}
|
|
}
|
|
|
|
void GX2SetSpecialState(GX2_SPECIAL_STATE stateId, uint32 isEnabled)
|
|
{
|
|
if (stateId == GX2_SPECIAL_STATE::FAST_CLEAR)
|
|
{
|
|
_setSpecialState0(isEnabled != 0);
|
|
}
|
|
else if (stateId == GX2_SPECIAL_STATE::FAST_CLEAR_HIZ)
|
|
{
|
|
// todo
|
|
// enables additional flags for special state 0
|
|
}
|
|
else
|
|
{
|
|
// legacy style
|
|
gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_SPECIAL_STATE, 2));
|
|
gx2WriteGather_submitU32AsBE((uint32)stateId); // state id
|
|
gx2WriteGather_submitU32AsBE(isEnabled); // enable/disable bool
|
|
}
|
|
}
|
|
|
|
void GX2StateInit()
|
|
{
|
|
cafeExportRegister("gx2", GX2InitAlphaTestReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetAlphaTestReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetAlphaTest, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitColorControlReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetColorControl, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetColorControlReg, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitPolygonControlReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetPolygonControlReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetPolygonControl, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetCullOnlyControl, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitPolygonOffsetReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetPolygonOffsetReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetPolygonOffset, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2SetRasterizerClipControl, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetRasterizerClipControlHalfZ, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetRasterizerClipControlEx, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitViewportReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetViewportReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetViewport, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitScissorReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetScissorReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2GetScissorReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetScissor, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitDepthStencilControlReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetDepthStencilControlReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2GetDepthStencilControlReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetDepthOnlyControl, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetDepthStencilControl, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitStencilMaskReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetStencilMask, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetStencilMaskReg, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2SetPrimitiveRestartIndex, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitTargetChannelMasksReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetTargetChannelMasksReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetTargetChannelMasks, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2GetTargetChannelMasksReg, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitBlendControlReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetBlendControlReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetBlendControl, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitBlendConstantColorReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetBlendConstantColorReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetBlendConstantColor, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2InitHiStencilInfoRegs, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2InitPointSizeReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetPointSizeReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetPointSize, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2InitPointLimitsReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetPointLimitsReg, LogType::GX2);
|
|
cafeExportRegister("gx2", GX2SetPointLimits, LogType::GX2);
|
|
|
|
cafeExportRegister("gx2", GX2SetSpecialState, LogType::GX2);
|
|
}
|
|
|
|
}
|