CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/GPU/Vulkan/StateMappingVulkan.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>1819#include "Common/GPU/Vulkan/VulkanLoader.h"20#include "Common/GPU/Vulkan/VulkanRenderManager.h"2122#include "Common/Data/Convert/SmallDataConvert.h"23#include "GPU/Math3D.h"24#include "GPU/GPUState.h"25#include "GPU/ge_constants.h"26#include "GPU/Common/GPUStateUtils.h"27#include "Core/System.h"28#include "Core/Config.h"29#include "GPU/Vulkan/GPU_Vulkan.h"30#include "GPU/Vulkan/PipelineManagerVulkan.h"31#include "GPU/Vulkan/FramebufferManagerVulkan.h"32#include "GPU/Vulkan/ShaderManagerVulkan.h"33#include "GPU/Vulkan/DrawEngineVulkan.h"3435// These tables all fit into u8s.36static const VkBlendFactor vkBlendFactorLookup[(size_t)BlendFactor::COUNT] = {37VK_BLEND_FACTOR_ZERO,38VK_BLEND_FACTOR_ONE,39VK_BLEND_FACTOR_SRC_COLOR,40VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,41VK_BLEND_FACTOR_DST_COLOR,42VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,43VK_BLEND_FACTOR_SRC_ALPHA,44VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,45VK_BLEND_FACTOR_DST_ALPHA,46VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,47VK_BLEND_FACTOR_CONSTANT_COLOR,48VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,49VK_BLEND_FACTOR_CONSTANT_ALPHA,50VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,51VK_BLEND_FACTOR_SRC1_COLOR,52VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR,53VK_BLEND_FACTOR_SRC1_ALPHA,54VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA,55VK_BLEND_FACTOR_MAX_ENUM,56};5758static const VkBlendOp vkBlendEqLookup[(size_t)BlendEq::COUNT] = {59VK_BLEND_OP_ADD,60VK_BLEND_OP_SUBTRACT,61VK_BLEND_OP_REVERSE_SUBTRACT,62VK_BLEND_OP_MIN,63VK_BLEND_OP_MAX,64};6566static const VkCullModeFlagBits cullingMode[] = {67VK_CULL_MODE_BACK_BIT,68VK_CULL_MODE_FRONT_BIT,69};7071static const VkCompareOp compareOps[] = {72VK_COMPARE_OP_NEVER,73VK_COMPARE_OP_ALWAYS,74VK_COMPARE_OP_EQUAL,75VK_COMPARE_OP_NOT_EQUAL,76VK_COMPARE_OP_LESS,77VK_COMPARE_OP_LESS_OR_EQUAL,78VK_COMPARE_OP_GREATER,79VK_COMPARE_OP_GREATER_OR_EQUAL,80};8182static const VkStencilOp stencilOps[] = {83VK_STENCIL_OP_KEEP,84VK_STENCIL_OP_ZERO,85VK_STENCIL_OP_REPLACE,86VK_STENCIL_OP_INVERT,87VK_STENCIL_OP_INCREMENT_AND_CLAMP,88VK_STENCIL_OP_DECREMENT_AND_CLAMP,89VK_STENCIL_OP_KEEP, // reserved90VK_STENCIL_OP_KEEP, // reserved91};9293static const VkPrimitiveTopology primToVulkan[8] = {94VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // We convert points to triangles.95VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // We convert lines to triangles.96VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // We convert line strips to triangles.97VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,98VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,99VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,100VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // Vulkan doesn't do quads. We could do strips with restart-index though. We could also do RECT primitives in the geometry shader.101};102103// These are actually the same exact values/order/etc. as the GE ones, but for clarity...104static const VkLogicOp logicOps[] = {105VK_LOGIC_OP_CLEAR,106VK_LOGIC_OP_AND,107VK_LOGIC_OP_AND_REVERSE,108VK_LOGIC_OP_COPY,109VK_LOGIC_OP_AND_INVERTED,110VK_LOGIC_OP_NO_OP,111VK_LOGIC_OP_XOR,112VK_LOGIC_OP_OR,113VK_LOGIC_OP_NOR,114VK_LOGIC_OP_EQUIVALENT,115VK_LOGIC_OP_INVERT,116VK_LOGIC_OP_OR_REVERSE,117VK_LOGIC_OP_COPY_INVERTED,118VK_LOGIC_OP_OR_INVERTED,119VK_LOGIC_OP_NAND,120VK_LOGIC_OP_SET,121};122123// In Vulkan, we simply collect all the state together into a "pipeline key" - we don't actually set any state here124// (the caller is responsible for setting the little dynamic state that is supported, dynState).125void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState) {126key.topology = primToVulkan[prim];127128bool useBufferedRendering = framebufferManager_->UseBufferedRendering();129130if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {131if (gstate.isModeClear()) {132key.logicOpEnable = false;133key.logicOp = VK_LOGIC_OP_CLEAR;134key.blendEnable = false;135key.blendOpColor = VK_BLEND_OP_ADD;136key.blendOpAlpha = VK_BLEND_OP_ADD;137key.srcColor = VK_BLEND_FACTOR_ONE;138key.srcAlpha = VK_BLEND_FACTOR_ONE;139key.destColor = VK_BLEND_FACTOR_ZERO;140key.destAlpha = VK_BLEND_FACTOR_ZERO;141dynState.useBlendColor = false;142143// Color Mask144bool colorMask = gstate.isClearModeColorMask();145bool alphaMask = gstate.isClearModeAlphaMask();146key.colorWriteMask = (colorMask ? (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT) : 0) | (alphaMask ? VK_COLOR_COMPONENT_A_BIT : 0);147} else {148pipelineState_.Convert(draw_->GetShaderLanguageDesc().bitwiseOps);149GenericMaskState &maskState = pipelineState_.maskState;150GenericBlendState &blendState = pipelineState_.blendState;151GenericLogicState &logicState = pipelineState_.logicState;152153if (pipelineState_.FramebufferRead() && useBufferedRendering) {154ApplyFramebufferRead(&fboTexBindState_);155// The shader takes over the responsibility for blending, so recompute.156// We might still end up using blend to write something to alpha.157ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState);158dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE;159gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);160} else {161if (fboTexBound_) {162boundSecondary_ = VK_NULL_HANDLE;163fboTexBound_ = false;164dirtyRequiresRecheck_ |= DIRTY_FRAGMENTSHADER_STATE;165gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);166}167}168169if (blendState.blendEnabled) {170key.blendEnable = true;171key.blendOpColor = vkBlendEqLookup[(size_t)blendState.eqColor];172key.blendOpAlpha = vkBlendEqLookup[(size_t)blendState.eqAlpha];173key.srcColor = vkBlendFactorLookup[(size_t)blendState.srcColor];174key.srcAlpha = vkBlendFactorLookup[(size_t)blendState.srcAlpha];175key.destColor = vkBlendFactorLookup[(size_t)blendState.dstColor];176key.destAlpha = vkBlendFactorLookup[(size_t)blendState.dstAlpha];177if (blendState.dirtyShaderBlendFixValues) {178dirtyRequiresRecheck_ |= DIRTY_SHADERBLEND;179gstate_c.Dirty(DIRTY_SHADERBLEND);180}181dynState.useBlendColor = blendState.useBlendColor;182if (blendState.useBlendColor) {183dynState.blendColor = blendState.blendColor;184}185} else {186key.blendEnable = false;187key.blendOpColor = VK_BLEND_OP_ADD;188key.blendOpAlpha = VK_BLEND_OP_ADD;189key.srcColor = VK_BLEND_FACTOR_ONE;190key.srcAlpha = VK_BLEND_FACTOR_ONE;191key.destColor = VK_BLEND_FACTOR_ZERO;192key.destAlpha = VK_BLEND_FACTOR_ZERO;193dynState.useBlendColor = false;194}195196key.colorWriteMask = maskState.channelMask; // flags match197198if (logicState.logicOpEnabled) {199key.logicOpEnable = true;200key.logicOp = logicOps[(int)logicState.logicOp];201} else {202key.logicOpEnable = false;203key.logicOp = VK_LOGIC_OP_COPY;204}205206// Workaround proposed in #10421, for bug where the color write mask is not applied correctly on Adreno.207if ((gstate.pmskc & 0x00FFFFFF) == 0x00FFFFFF && g_Config.bVendorBugChecksEnabled && draw_->GetBugs().Has(Draw::Bugs::COLORWRITEMASK_BROKEN_WITH_DEPTHTEST)) {208key.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;209if (!key.blendEnable) {210bool writeAlpha = maskState.channelMask & 8;211key.blendEnable = true;212key.blendOpAlpha = VK_BLEND_OP_ADD;213key.srcAlpha = writeAlpha ? VK_BLEND_FACTOR_ONE : VK_BLEND_FACTOR_ZERO;214key.destAlpha = writeAlpha ? VK_BLEND_FACTOR_ZERO : VK_BLEND_FACTOR_ONE;215}216key.blendOpColor = VK_BLEND_OP_ADD;217key.srcColor = VK_BLEND_FACTOR_ZERO;218key.destColor = VK_BLEND_FACTOR_ONE;219}220}221}222223if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) {224bool wantCull = !gstate.isModeClear() && prim != GE_PRIM_RECTANGLES && prim > GE_PRIM_LINE_STRIP && gstate.isCullEnabled();225key.cullMode = wantCull ? (gstate.getCullMode() ? VK_CULL_MODE_FRONT_BIT : VK_CULL_MODE_BACK_BIT) : VK_CULL_MODE_NONE;226227if (gstate.isModeClear() || gstate.isModeThrough()) {228// TODO: Might happen in clear mode if not through...229key.depthClampEnable = false;230} else {231if (gstate.getDepthRangeMin() == 0 || gstate.getDepthRangeMax() == 65535) {232// TODO: Still has a bug where we clamp to depth range if one is not the full range.233// But the alternate is not clamping in either direction...234key.depthClampEnable = gstate.isDepthClampEnabled() && gstate_c.Use(GPU_USE_DEPTH_CLAMP);235} else {236// We just want to clip in this case, the clamp would be clipped anyway.237key.depthClampEnable = false;238}239}240}241242if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {243GenericStencilFuncState stencilState;244ConvertStencilFuncState(stencilState);245246if (gstate.isModeClear()) {247key.depthTestEnable = true;248key.depthCompareOp = VK_COMPARE_OP_ALWAYS;249key.depthWriteEnable = gstate.isClearModeDepthMask();250251// Stencil Test252bool alphaMask = gstate.isClearModeAlphaMask();253if (alphaMask) {254key.stencilTestEnable = true;255key.stencilCompareOp = VK_COMPARE_OP_ALWAYS;256key.stencilPassOp = VK_STENCIL_OP_REPLACE;257key.stencilFailOp = VK_STENCIL_OP_REPLACE;258key.stencilDepthFailOp = VK_STENCIL_OP_REPLACE;259dynState.useStencil = true;260// In clear mode, the stencil value is set to the alpha value of the vertex.261// A normal clear will be 2 points, the second point has the color.262// We override this value in the pipeline from software transform for clear rectangles.263dynState.stencilRef = 0xFF;264// But we still apply the stencil write mask.265dynState.stencilWriteMask = stencilState.writeMask;266} else {267key.stencilTestEnable = false;268key.stencilCompareOp = VK_COMPARE_OP_ALWAYS;269key.stencilPassOp = VK_STENCIL_OP_REPLACE;270key.stencilFailOp = VK_STENCIL_OP_REPLACE;271key.stencilDepthFailOp = VK_STENCIL_OP_REPLACE;272dynState.useStencil = false;273}274} else {275// Depth Test276if (!IsDepthTestEffectivelyDisabled()) {277key.depthTestEnable = true;278key.depthCompareOp = compareOps[gstate.getDepthTestFunction()];279key.depthWriteEnable = gstate.isDepthWriteEnabled();280UpdateEverUsedEqualDepth(gstate.getDepthTestFunction());281} else {282key.depthTestEnable = false;283key.depthWriteEnable = false;284key.depthCompareOp = VK_COMPARE_OP_ALWAYS;285}286287// Stencil Test288if (stencilState.enabled) {289key.stencilTestEnable = true;290key.stencilCompareOp = compareOps[stencilState.testFunc];291key.stencilPassOp = stencilOps[stencilState.zPass];292key.stencilFailOp = stencilOps[stencilState.sFail];293key.stencilDepthFailOp = stencilOps[stencilState.zFail];294dynState.useStencil = true;295dynState.stencilRef = stencilState.testRef;296dynState.stencilCompareMask = stencilState.testMask;297dynState.stencilWriteMask = stencilState.writeMask;298299// Nasty special case for Spongebob and similar where it tries to write zeros to alpha/stencil during300// depth-fail. We can't write to alpha then because the pixel is killed. However, we can invert the depth301// test and modify the alpha function...302if (SpongebobDepthInverseConditions(stencilState)) {303key.blendEnable = true;304key.blendOpAlpha = VK_BLEND_OP_ADD;305key.blendOpColor = VK_BLEND_OP_ADD;306key.srcColor = VK_BLEND_FACTOR_ZERO;307key.destColor = VK_BLEND_FACTOR_ZERO;308key.logicOpEnable = false;309key.srcAlpha = VK_BLEND_FACTOR_ZERO;310key.destAlpha = VK_BLEND_FACTOR_ZERO;311key.colorWriteMask = VK_COLOR_COMPONENT_A_BIT;312key.depthCompareOp = VK_COMPARE_OP_LESS; // Inverse of GREATER_EQUAL313key.stencilCompareOp = VK_COMPARE_OP_ALWAYS;314// Invert315key.stencilPassOp = VK_STENCIL_OP_ZERO;316key.stencilFailOp = VK_STENCIL_OP_ZERO;317key.stencilDepthFailOp = VK_STENCIL_OP_KEEP;318319dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE;320gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);321}322} else {323key.stencilTestEnable = false;324key.stencilCompareOp = VK_COMPARE_OP_ALWAYS;325key.stencilPassOp = VK_STENCIL_OP_REPLACE;326key.stencilFailOp = VK_STENCIL_OP_REPLACE;327key.stencilDepthFailOp = VK_STENCIL_OP_REPLACE;328dynState.useStencil = false;329}330}331}332333if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {334ViewportAndScissor vpAndScissor;335ConvertViewportAndScissor(useBufferedRendering,336fbManager.GetRenderWidth(), fbManager.GetRenderHeight(),337fbManager.GetTargetBufferWidth(), fbManager.GetTargetBufferHeight(),338vpAndScissor);339UpdateCachedViewportState(vpAndScissor);340341float depthMin = vpAndScissor.depthRangeMin;342float depthMax = vpAndScissor.depthRangeMax;343344if (depthMin < 0.0f) depthMin = 0.0f;345if (depthMax > 1.0f) depthMax = 1.0f;346347VkViewport &vp = dynState.viewport;348vp.x = vpAndScissor.viewportX;349vp.y = vpAndScissor.viewportY;350vp.width = vpAndScissor.viewportW;351vp.height = vpAndScissor.viewportH;352vp.minDepth = vpAndScissor.depthRangeMin;353vp.maxDepth = vpAndScissor.depthRangeMax;354355ScissorRect &scissor = dynState.scissor;356scissor.x = vpAndScissor.scissorX;357scissor.y = vpAndScissor.scissorY;358scissor.width = std::max(0, vpAndScissor.scissorW);359scissor.height = std::max(0, vpAndScissor.scissorH);360}361}362363void DrawEngineVulkan::BindShaderBlendTex() {364// TODO: At this point, we know if the vertices are full alpha or not.365// Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?366if (!gstate.isModeClear()) {367if (fboTexBindState_ == FBO_TEX_COPY_BIND_TEX) {368VirtualFramebuffer *curRenderVfb = framebufferManager_->GetCurrentRenderVFB();369bool bindResult = framebufferManager_->BindFramebufferAsColorTexture(1, curRenderVfb, BINDFBCOLOR_MAY_COPY | BINDFBCOLOR_UNCACHED, Draw::ALL_LAYERS);370_dbg_assert_(bindResult);371boundSecondary_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE1_IMAGEVIEW);372fboTexBound_ = true;373fboTexBindState_ = FBO_TEX_NONE;374375// Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.376dirtyRequiresRecheck_ |= DIRTY_BLEND_STATE;377} else {378boundSecondary_ = VK_NULL_HANDLE;379}380} else {381boundSecondary_ = VK_NULL_HANDLE;382}383}384385void DrawEngineVulkan::ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant) {386if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {387renderManager->SetScissor(dynState_.scissor.x, dynState_.scissor.y, dynState_.scissor.width, dynState_.scissor.height);388renderManager->SetViewport(dynState_.viewport);389}390if ((gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE) && dynState_.useStencil) || applyStencilRef) {391renderManager->SetStencilParams(dynState_.stencilWriteMask, dynState_.stencilCompareMask, applyStencilRef ? stencilRef : dynState_.stencilRef);392}393if (gstate_c.IsDirty(DIRTY_BLEND_STATE) && useBlendConstant) {394renderManager->SetBlendFactor(dynState_.blendColor);395}396}397398399