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/Common/Draw2D.cpp
Views: 1401
// Copyright (c) 2014- 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 "Common/GPU/Shader.h"18#include "Common/GPU/ShaderWriter.h"19#include "Common/GPU/thin3d.h"20#include "Core/Config.h"21#include "Core/ConfigValues.h"22#include "GPU/Common/Draw2D.h"23#include "GPU/Common/DrawEngineCommon.h"24#include "GPU/Common/FramebufferManagerCommon.h"25#include "GPU/Common/TextureCacheCommon.h"26#include "GPU/Common/GPUStateUtils.h"2728static const InputDef inputs[2] = {29{ "vec2", "a_position", Draw::SEM_POSITION },30{ "vec2", "a_texcoord0", Draw::SEM_TEXCOORD0 },31};3233static const VaryingDef varyings[1] = {34{ "vec2", "v_texcoord", Draw::SEM_TEXCOORD0, 0, "highp" },35};3637static const SamplerDef samplers[1] = {38{ 0, "tex", SamplerFlags::ARRAY_ON_VULKAN },39};4041const UniformDef g_draw2Duniforms[5] = {42{ "vec2", "texSize", 0 },43{ "float", "scaleFactor", 1},44{ "float", "z_scale", 2 },45{ "float", "z_scale_inv", 3 },46{ "float", "z_offset", 4 },47};4849struct Draw2DUB {50float texSizeX;51float texSizeY;52float scaleFactor;53float zScale;54float zScaleInv;55float zOffset;56};5758const UniformBufferDesc draw2DUBDesc{ sizeof(Draw2DUB), {59{ "texSize", -1, 0, UniformType::FLOAT2, 0 },60{ "scaleFactor", -1, 1, UniformType::FLOAT1, 8 },61{ "z_scale", -1, 1, UniformType::FLOAT1, 12 },62{ "z_scale_inv", -1, 1, UniformType::FLOAT1, 16 },63{ "z_offset", -1, 1, UniformType::FLOAT1, 20 },64} };6566Draw2DPipelineInfo GenerateDraw2DCopyColorFs(ShaderWriter &writer) {67writer.DeclareSamplers(samplers);68writer.BeginFSMain(Slice<UniformDef>::empty(), varyings);69writer.C(" vec4 outColor = ").SampleTexture2D("tex", "v_texcoord.xy").C(";\n");70writer.EndFSMain("outColor");7172return Draw2DPipelineInfo{73"draw2d_copy_color",74RASTER_COLOR,75RASTER_COLOR,76};77}7879Draw2DPipelineInfo GenerateDraw2DCopyColorRect2LinFs(ShaderWriter &writer) {80writer.DeclareSamplers(samplers);81writer.BeginFSMain(g_draw2Duniforms, varyings);82writer.C(" vec2 tSize = texSize / scaleFactor;\n");83writer.C(" vec2 pixels = v_texcoord * tSize;\n");84writer.C(" float u = mod(pixels.x, tSize.x);\n");85writer.C(" float v = floor(pixels.x / tSize.x);\n");86writer.C(" vec4 outColor = ").SampleTexture2D("tex", "vec2(u, v) / tSize").C(";\n");87writer.EndFSMain("outColor");8889return Draw2DPipelineInfo{90"draw2d_copy_color_rect2lin",91RASTER_COLOR,92RASTER_COLOR,93};94}9596Draw2DPipelineInfo GenerateDraw2DCopyDepthFs(ShaderWriter &writer) {97writer.SetFlags(ShaderWriterFlags::FS_WRITE_DEPTH);98writer.DeclareSamplers(samplers);99writer.BeginFSMain(Slice<UniformDef>::empty(), varyings);100writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");101writer.C(" gl_FragDepth = ").SampleTexture2D("tex", "v_texcoord.xy").C(".x;\n");102writer.EndFSMain("outColor");103104return Draw2DPipelineInfo{105"draw2d_copy_depth",106RASTER_DEPTH, // Unused in this case, I think.107RASTER_DEPTH,108};109}110111Draw2DPipelineInfo GenerateDraw2DEncodeDepthFs(ShaderWriter &writer) {112writer.SetFlags(ShaderWriterFlags::FS_WRITE_DEPTH);113writer.HighPrecisionFloat();114writer.DeclareSamplers(samplers);115writer.BeginFSMain(g_draw2Duniforms, varyings);116writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");117writer.C(" float depthValue = ").SampleTexture2D("tex", "v_texcoord.xy").C(".x;\n");118writer.C(" gl_FragDepth = (depthValue * z_scale_inv) + z_offset;\n");119writer.EndFSMain("outColor");120121return Draw2DPipelineInfo{122"draw2d_copy_r16_to_depth",123RASTER_COLOR,124RASTER_DEPTH,125};126}127128Draw2DPipelineInfo GenerateDraw2D565ToDepthFs(ShaderWriter &writer) {129writer.SetFlags(ShaderWriterFlags::FS_WRITE_DEPTH);130writer.HighPrecisionFloat();131writer.DeclareSamplers(samplers);132writer.BeginFSMain(g_draw2Duniforms, varyings);133writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");134// Unlike when just copying a depth buffer, here we're generating new depth values so we'll135// have to apply the scaling.136DepthScaleFactors factors = GetDepthScaleFactors(gstate_c.UseFlags());137writer.C(" vec3 rgb = ").SampleTexture2D("tex", "v_texcoord.xy").C(".xyz;\n");138writer.F(" float depthValue = ((floor(rgb.x * 31.99) + floor(rgb.y * 63.99) * 32.0 + floor(rgb.z * 31.99) * 2048.0)) / 65535.0; \n");139writer.C(" gl_FragDepth = (depthValue * z_scale_inv) + z_offset;\n");140writer.EndFSMain("outColor");141142return Draw2DPipelineInfo{143"draw2d_565_to_depth",144RASTER_COLOR,145RASTER_DEPTH,146};147}148149Draw2DPipelineInfo GenerateDraw2D565ToDepthDeswizzleFs(ShaderWriter &writer) {150writer.SetFlags(ShaderWriterFlags::FS_WRITE_DEPTH);151writer.HighPrecisionFloat();152writer.DeclareSamplers(samplers);153writer.BeginFSMain(g_draw2Duniforms, varyings);154writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");155// Unlike when just copying a depth buffer, here we're generating new depth values so we'll156// have to apply the scaling.157DepthScaleFactors factors = GetDepthScaleFactors(gstate_c.UseFlags());158writer.C(" vec2 tsize = texSize;\n");159writer.C(" vec2 coord = v_texcoord * tsize;\n");160writer.F(" float strip = 4.0 * scaleFactor;\n");161writer.C(" float in_strip = mod(coord.y, strip);\n");162writer.C(" coord.y = coord.y - in_strip + strip - in_strip;\n");163writer.C(" coord /= tsize;\n");164writer.C(" highp vec3 rgb = ").SampleTexture2D("tex", "coord").C(".xyz;\n");165writer.F(" highp float depthValue = floor(rgb.x * 31.99) + floor(rgb.y * 63.99) * 32.0 + floor(rgb.z * 31.99) * 2048.0; \n");166writer.C(" gl_FragDepth = z_offset + ((depthValue / 65535.0) * z_scale_inv);\n");167writer.EndFSMain("outColor");168169return Draw2DPipelineInfo{170"draw2d_565_to_depth_deswizzle",171RASTER_COLOR,172RASTER_DEPTH173};174}175176void GenerateDraw2DVS(ShaderWriter &writer) {177writer.BeginVSMain(inputs, Slice<UniformDef>::empty(), varyings);178179writer.C(" v_texcoord = a_texcoord0;\n"); // yes, this should be right. Should be 2.0 in the far corners.180writer.C(" gl_Position = vec4(a_position, 0.0, 1.0);\n");181182writer.EndVSMain(varyings);183}184185template <typename T>186static void DoRelease(T *&obj) {187if (obj)188obj->Release();189obj = nullptr;190}191192void Draw2D::DeviceLost() {193DoRelease(draw2DVs_);194DoRelease(draw2DSamplerLinear_);195DoRelease(draw2DSamplerNearest_);196draw_ = nullptr;197}198199void Draw2D::DeviceRestore(Draw::DrawContext *draw) {200draw_ = draw;201}202203void Draw2D::Ensure2DResources() {204using namespace Draw;205206const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc();207208if (!draw2DVs_) {209char *vsCode = new char[8192];210ShaderWriterFlags flags = ShaderWriterFlags::NONE;211if (gstate_c.Use(GPU_USE_SINGLE_PASS_STEREO)) {212// Hm, we're compiling the vertex shader here, probably don't need this...213flags = ShaderWriterFlags::FS_AUTO_STEREO;214}215ShaderWriter writer(vsCode, shaderLanguageDesc, ShaderStage::Vertex);216GenerateDraw2DVS(writer);217_assert_msg_(strlen(vsCode) < 8192, "Draw2D VS length error: %d", (int)strlen(vsCode));218draw2DVs_ = draw_->CreateShaderModule(ShaderStage::Vertex, shaderLanguageDesc.shaderLanguage, (const uint8_t *)vsCode, strlen(vsCode), "draw2d_vs");219_assert_(draw2DVs_);220delete[] vsCode;221}222223if (!draw2DSamplerLinear_) {224SamplerStateDesc descLinear{};225descLinear.magFilter = TextureFilter::LINEAR;226descLinear.minFilter = TextureFilter::LINEAR;227descLinear.mipFilter = TextureFilter::LINEAR;228descLinear.wrapU = TextureAddressMode::CLAMP_TO_EDGE;229descLinear.wrapV = TextureAddressMode::CLAMP_TO_EDGE;230descLinear.wrapW = TextureAddressMode::CLAMP_TO_EDGE;231draw2DSamplerLinear_ = draw_->CreateSamplerState(descLinear);232}233234if (!draw2DSamplerNearest_) {235SamplerStateDesc descNearest{};236descNearest.magFilter = TextureFilter::NEAREST;237descNearest.minFilter = TextureFilter::NEAREST;238descNearest.mipFilter = TextureFilter::NEAREST;239descNearest.wrapU = TextureAddressMode::CLAMP_TO_EDGE;240descNearest.wrapV = TextureAddressMode::CLAMP_TO_EDGE;241descNearest.wrapW = TextureAddressMode::CLAMP_TO_EDGE;242draw2DSamplerNearest_ = draw_->CreateSamplerState(descNearest);243}244}245246Draw2DPipeline *Draw2D::Create2DPipeline(std::function<Draw2DPipelineInfo (ShaderWriter &)> generate) {247Ensure2DResources();248249using namespace Draw;250const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc();251252char *fsCode = new char[8192];253ShaderWriterFlags flags = ShaderWriterFlags::NONE;254if (gstate_c.Use(GPU_USE_SINGLE_PASS_STEREO)) {255flags = ShaderWriterFlags::FS_AUTO_STEREO;256}257ShaderWriter writer(fsCode, shaderLanguageDesc, ShaderStage::Fragment, Slice<const char *>::empty(), flags);258Draw2DPipelineInfo info = generate(writer);259_assert_msg_(strlen(fsCode) < 8192, "Draw2D FS length error: %d", (int)strlen(fsCode));260261ShaderModule *fs = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), info.tag);262263_assert_msg_(fs, "Failed to create shader module!\n%s", fsCode);264265// verts have positions in 2D clip coordinates.266static const InputLayoutDesc desc = {26716,268{269{ SEM_POSITION, DataFormat::R32G32_FLOAT, 0 },270{ SEM_TEXCOORD0, DataFormat::R32G32_FLOAT, 8 },271},272};273InputLayout *inputLayout = draw_->CreateInputLayout(desc);274275BlendState *blend = draw_->CreateBlendState({ false, info.writeChannel == RASTER_COLOR ? 0xF : 0x0 });276277DepthStencilStateDesc dsDesc{};278if (info.writeChannel == RASTER_DEPTH) {279dsDesc.depthTestEnabled = true;280dsDesc.depthWriteEnabled = true;281dsDesc.depthCompare = Draw::Comparison::ALWAYS;282}283284DepthStencilState *depthStencil = draw_->CreateDepthStencilState(dsDesc);285RasterState *rasterNoCull = draw_->CreateRasterState({});286287PipelineDesc pipelineDesc{288Primitive::TRIANGLE_STRIP,289{ draw2DVs_, fs },290inputLayout,291depthStencil,292blend,293rasterNoCull,294&draw2DUBDesc,295info.samplers.is_empty() ? samplers : info.samplers,296};297298Draw::Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc, info.tag);299300fs->Release();301302rasterNoCull->Release();303blend->Release();304depthStencil->Release();305inputLayout->Release();306307return new Draw2DPipeline {308pipeline,309info,310fsCode,311};312}313314void Draw2D::Blit(Draw2DPipeline *pipeline, float srcX1, float srcY1, float srcX2, float srcY2, float dstX1, float dstY1, float dstX2, float dstY2, float srcWidth, float srcHeight, float dstWidth, float dstHeight, bool linear, int scaleFactor) {315float dX = 1.0f / (float)dstWidth;316float dY = 1.0f / (float)dstHeight;317float sX = 1.0f / (float)srcWidth;318float sY = 1.0f / (float)srcHeight;319float xOffset = 0.0f;320float yOffset = 0.0f;321if (draw_->GetDeviceCaps().requiresHalfPixelOffset) {322xOffset = -dX * 0.5f;323yOffset = -dY * 0.5f;324}325Draw2DVertex vtx[4] = {326{ -1.0f + 2.0f * dX * dstX1 + xOffset, -(1.0f - 2.0f * dY * dstY1) + yOffset, sX * srcX1, sY * srcY1 },327{ -1.0f + 2.0f * dX * dstX2 + xOffset, -(1.0f - 2.0f * dY * dstY1) + yOffset, sX * srcX2, sY * srcY1 },328{ -1.0f + 2.0f * dX * dstX1 + xOffset, -(1.0f - 2.0f * dY * dstY2) + yOffset, sX * srcX1, sY * srcY2 },329{ -1.0f + 2.0f * dX * dstX2 + xOffset, -(1.0f - 2.0f * dY * dstY2) + yOffset, sX * srcX2, sY * srcY2 },330};331332DrawStrip2D(nullptr, vtx, 4, linear, pipeline, srcWidth, srcHeight, scaleFactor);333}334335void Draw2D::DrawStrip2D(Draw::Texture *tex, const Draw2DVertex *verts, int vertexCount, bool linearFilter, Draw2DPipeline *pipeline, float texW, float texH, int scaleFactor) {336using namespace Draw;337338_dbg_assert_(pipeline);339340if (pipeline->info.writeChannel == RASTER_DEPTH) {341_dbg_assert_(draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported);342343// We don't filter inputs when writing depth, results will be bad.344linearFilter = false;345}346347Draw2DUB ub;348ub.texSizeX = tex ? tex->Width() : texW;349ub.texSizeY = tex ? tex->Height() : texH;350ub.scaleFactor = (float)scaleFactor;351352DepthScaleFactors zScaleFactors = GetDepthScaleFactors(gstate_c.UseFlags());353ub.zScale = zScaleFactors.Scale();354ub.zScaleInv = 1.0f / ub.zScale;355ub.zOffset = zScaleFactors.Offset();356357draw_->BindPipeline(pipeline->pipeline);358draw_->UpdateDynamicUniformBuffer(&ub, sizeof(ub));359360if (tex) {361// This won't work since all the shaders above expect array textures on Vulkan.362draw_->BindTextures(TEX_SLOT_PSP_TEXTURE, 1, &tex);363}364draw_->BindSamplerStates(TEX_SLOT_PSP_TEXTURE, 1, linearFilter ? &draw2DSamplerLinear_ : &draw2DSamplerNearest_);365draw_->DrawUP(verts, vertexCount);366367draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);368369gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE | DIRTY_VERTEXSHADER_STATE);370}371372Draw2DPipeline *FramebufferManagerCommon::Get2DPipeline(Draw2DShader shader) {373using namespace Draw;374375const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc();376377Draw2DPipeline *pipeline = nullptr;378379switch (shader) {380case DRAW2D_COPY_COLOR:381if (!draw2DPipelineCopyColor_) {382draw2DPipelineCopyColor_ = draw2D_.Create2DPipeline(&GenerateDraw2DCopyColorFs);383}384pipeline = draw2DPipelineCopyColor_;385break;386387case DRAW2D_COPY_COLOR_RECT2LIN:388if (!draw2DPipelineColorRect2Lin_) {389draw2DPipelineColorRect2Lin_ = draw2D_.Create2DPipeline(&GenerateDraw2DCopyColorRect2LinFs);390}391pipeline = draw2DPipelineColorRect2Lin_;392break;393case DRAW2D_COPY_DEPTH:394if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) {395// Can't do it396return nullptr;397}398if (!draw2DPipelineCopyDepth_) {399draw2DPipelineCopyDepth_ = draw2D_.Create2DPipeline(&GenerateDraw2DCopyDepthFs);400}401pipeline = draw2DPipelineCopyDepth_;402break;403404case DRAW2D_ENCODE_R16_TO_DEPTH:405if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) {406// Can't do it407return nullptr;408}409if (!draw2DPipelineEncodeDepth_) {410draw2DPipelineEncodeDepth_ = draw2D_.Create2DPipeline(&GenerateDraw2DEncodeDepthFs);411}412pipeline = draw2DPipelineEncodeDepth_;413break;414415case DRAW2D_565_TO_DEPTH:416if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) {417// Can't do it418return nullptr;419}420if (!draw2DPipeline565ToDepth_) {421draw2DPipeline565ToDepth_ = draw2D_.Create2DPipeline(&GenerateDraw2D565ToDepthFs);422}423pipeline = draw2DPipeline565ToDepth_;424break;425426case DRAW2D_565_TO_DEPTH_DESWIZZLE:427if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) {428// Can't do it429return nullptr;430}431if (!draw2DPipeline565ToDepthDeswizzle_) {432draw2DPipeline565ToDepthDeswizzle_ = draw2D_.Create2DPipeline(&GenerateDraw2D565ToDepthDeswizzleFs);433}434pipeline = draw2DPipeline565ToDepthDeswizzle_;435break;436}437438return pipeline;439}440441442