// Copyright (c) 2014- PPSSPP Project. // This program 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, version 2.0 or later versions. // This program 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 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/GPU/Shader.h" #include "Common/GPU/ShaderWriter.h" #include "Common/GPU/thin3d.h" #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/Reporting.h" #include "GPU/Common/Draw2D.h" #include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/FramebufferManagerCommon.h" #include "GPU/Common/TextureCacheCommon.h" static const InputDef inputs[2] = { { "vec2", "a_position", Draw::SEM_POSITION }, { "vec2", "a_texcoord0", Draw::SEM_TEXCOORD0 }, }; static const VaryingDef varyings[1] = { { "vec2", "v_texcoord", Draw::SEM_TEXCOORD0, 0, "highp" }, }; static const SamplerDef samplers[1] = { { "tex" }, }; void GenerateDraw2DFs(ShaderWriter &writer) { writer.DeclareSamplers(samplers); writer.BeginFSMain(Slice::empty(), varyings, FSFLAG_NONE); writer.C(" vec4 outColor = ").SampleTexture2D("tex", "v_texcoord.xy").C(";\n"); writer.EndFSMain("outColor", FSFLAG_NONE); } void GenerateDraw2DDepthFs(ShaderWriter &writer) { writer.DeclareSamplers(samplers); writer.BeginFSMain(Slice::empty(), varyings, FSFLAG_WRITEDEPTH); writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n"); writer.C(" gl_FragDepth = ").SampleTexture2D("tex", "v_texcoord.xy").C(".x;\n"); writer.EndFSMain("outColor", FSFLAG_WRITEDEPTH); } void GenerateDraw2DVS(ShaderWriter &writer) { writer.BeginVSMain(inputs, Slice::empty(), varyings); writer.C(" v_texcoord = a_texcoord0;\n"); // yes, this should be right. Should be 2.0 in the far corners. writer.C(" gl_Position = vec4(a_position, 0.0, 1.0);\n"); writer.EndVSMain(varyings); } void FramebufferManagerCommon::Ensure2DResources() { using namespace Draw; const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc(); if (!draw2DVs_) { char *vsCode = new char[4000]; ShaderWriter writer(vsCode, shaderLanguageDesc, ShaderStage::Vertex); GenerateDraw2DVS(writer); draw2DVs_ = draw_->CreateShaderModule(ShaderStage::Vertex, shaderLanguageDesc.shaderLanguage, (const uint8_t *)vsCode, strlen(vsCode), "draw2d_vs"); _assert_(draw2DVs_); delete[] vsCode; } if (!draw2DSamplerLinear_) { SamplerStateDesc descLinear{}; descLinear.magFilter = TextureFilter::LINEAR; descLinear.minFilter = TextureFilter::LINEAR; descLinear.mipFilter = TextureFilter::LINEAR; descLinear.wrapU = TextureAddressMode::CLAMP_TO_EDGE; descLinear.wrapV = TextureAddressMode::CLAMP_TO_EDGE; draw2DSamplerLinear_ = draw_->CreateSamplerState(descLinear); } if (!draw2DSamplerNearest_) { SamplerStateDesc descNearest{}; descNearest.magFilter = TextureFilter::NEAREST; descNearest.minFilter = TextureFilter::NEAREST; descNearest.mipFilter = TextureFilter::NEAREST; descNearest.wrapU = TextureAddressMode::CLAMP_TO_EDGE; descNearest.wrapV = TextureAddressMode::CLAMP_TO_EDGE; draw2DSamplerNearest_ = draw_->CreateSamplerState(descNearest); } } Draw::Pipeline *FramebufferManagerCommon::Create2DPipeline(void (*generate)(ShaderWriter &)) { using namespace Draw; const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc(); char *fsCode = new char[4000]; ShaderWriter writer(fsCode, shaderLanguageDesc, ShaderStage::Fragment); generate(writer); ShaderModule *fs = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), "draw2d_fs"); delete[] fsCode; _assert_(fs); // verts have positions in 2D clip coordinates. static const InputLayoutDesc desc = { { { 16, false }, }, { { 0, SEM_POSITION, DataFormat::R32G32_FLOAT, 0 }, { 0, SEM_TEXCOORD0, DataFormat::R32G32_FLOAT, 8 }, }, }; InputLayout *inputLayout = draw_->CreateInputLayout(desc); BlendState *blendOff = draw_->CreateBlendState({ false, 0xF }); BlendState *blendDiscard = draw_->CreateBlendState({ false, 0x0 }); DepthStencilState *noDepthStencil = draw_->CreateDepthStencilState(DepthStencilStateDesc{}); RasterState *rasterNoCull = draw_->CreateRasterState({}); DepthStencilStateDesc dsWriteDesc{}; dsWriteDesc.depthTestEnabled = true; dsWriteDesc.depthWriteEnabled = true; dsWriteDesc.depthCompare = Draw::Comparison::ALWAYS; DepthStencilState *depthWriteAlways = draw_->CreateDepthStencilState(dsWriteDesc); PipelineDesc pipelineDesc{ Primitive::TRIANGLE_STRIP, { draw2DVs_, fs }, inputLayout, noDepthStencil, blendOff, rasterNoCull, nullptr, }; Draw::Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc); fs->Release(); rasterNoCull->Release(); blendOff->Release(); blendDiscard->Release(); noDepthStencil->Release(); depthWriteAlways->Release(); inputLayout->Release(); return pipeline; } void FramebufferManagerCommon::DrawStrip2D(Draw::Texture *tex, Draw2DVertex *verts, int vertexCount, bool linearFilter, RasterChannel channel) { using namespace Draw; Ensure2DResources(); const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc(); switch (channel) { case RASTER_COLOR: if (!draw2DPipelineColor_) { draw2DPipelineColor_ = Create2DPipeline(&GenerateDraw2DFs); } draw_->BindPipeline(draw2DPipelineColor_); break; case RASTER_DEPTH: if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) { // Can't do it return; } if (!draw2DPipelineDepth_) { draw2DPipelineDepth_ = Create2DPipeline(&GenerateDraw2DFs); } draw_->BindPipeline(draw2DPipelineDepth_); break; } if (tex) { draw_->BindTextures(TEX_SLOT_PSP_TEXTURE, 1, &tex); } draw_->BindSamplerStates(TEX_SLOT_PSP_TEXTURE, 1, linearFilter ? &draw2DSamplerLinear_ : &draw2DSamplerNearest_); draw_->DrawUP(verts, vertexCount); }