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/Windows/GEDebugger/VertexPreview.cpp
Views: 1401
// Copyright (c) 2013- 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/Math/lin/matrix4x4.h"18#include "Common/GPU/OpenGL/GLSLProgram.h"19#include "Common/GPU/OpenGL/GLFeatures.h"20#include "Windows/GEDebugger/GEDebugger.h"21#include "Windows/GEDebugger/SimpleGLWindow.h"22#include "Core/System.h"23#include "Core/Config.h"24#include "GPU/GPUInterface.h"25#include "GPU/Common/GPUDebugInterface.h"26#include "GPU/Common/SplineCommon.h"27#include "GPU/GPUState.h"28#include "Common/Log.h"29#include "Common/MemoryUtil.h"3031static const char preview_fs[] =32"#ifdef GL_ES\n"33"precision mediump float;\n"34"#endif\n"35"void main() {\n"36" gl_FragColor = vec4(1.0, 0.0, 0.0, 0.6);\n"37"}\n";3839static const char preview_vs[] =40"#version 120\n"41"attribute vec4 a_position;\n"42"uniform mat4 u_viewproj;\n"43"void main() {\n"44" gl_Position = u_viewproj * a_position;\n"45" gl_Position.z = 1.0;\n"46"}\n";4748static GLSLProgram *previewProgram = nullptr;49static GLSLProgram *texPreviewProgram = nullptr;5051static GLuint previewVao = 0;52static GLuint texPreviewVao = 0;53static GLuint vbuf = 0;54static GLuint ibuf = 0;5556static const GLuint glprim[8] = {57GL_POINTS,58GL_LINES,59GL_LINE_STRIP,60GL_TRIANGLES,61GL_TRIANGLE_STRIP,62GL_TRIANGLE_FAN,63// This is for RECTANGLES (see ExpandRectangles().)64GL_TRIANGLES,65};6667static void BindPreviewProgram(GLSLProgram *&prog) {68if (prog == nullptr) {69prog = glsl_create_source(preview_vs, preview_fs);70}7172glsl_bind(prog);73}7475static void SwapUVs(GPUDebugVertex &a, GPUDebugVertex &b) {76float tempu = a.u;77float tempv = a.v;78a.u = b.u;79a.v = b.v;80b.u = tempu;81b.v = tempv;82}8384static void RotateUVThrough(GPUDebugVertex v[4]) {85float x1 = v[2].x;86float x2 = v[0].x;87float y1 = v[2].y;88float y2 = v[0].y;8990if ((x1 < x2 && y1 > y2) || (x1 > x2 && y1 < y2))91SwapUVs(v[1], v[3]);92}9394static void ExpandRectangles(std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices, int &count, bool throughMode) {95static std::vector<GPUDebugVertex> newVerts;96static std::vector<u16> newInds;9798bool useInds = true;99size_t numInds = indices.size();100if (indices.empty()) {101useInds = false;102numInds = count;103}104105//rectangles always need 2 vertices, disregard the last one if there's an odd number106numInds = numInds & ~1;107108// Will need 4 coords and 6 points per rectangle (currently 2 each.)109newVerts.resize(numInds * 2);110newInds.resize(numInds * 3);111112u16 v = 0;113GPUDebugVertex *vert = &newVerts[0];114u16 *ind = &newInds[0];115for (size_t i = 0; i < numInds; i += 2) {116const auto &orig_tl = useInds ? vertices[indices[i + 0]] : vertices[i + 0];117const auto &orig_br = useInds ? vertices[indices[i + 1]] : vertices[i + 1];118119vert[0] = orig_br;120121// Top right.122vert[1] = orig_br;123vert[1].y = orig_tl.y;124vert[1].v = orig_tl.v;125126vert[2] = orig_tl;127128// Bottom left.129vert[3] = orig_br;130vert[3].x = orig_tl.x;131vert[3].u = orig_tl.u;132133// That's the four corners. Now process UV rotation.134// This is the same for through and non-through, since it's already transformed.135RotateUVThrough(vert);136137// Build the two 3 point triangles from our 4 coordinates.138*ind++ = v + 0;139*ind++ = v + 1;140*ind++ = v + 2;141*ind++ = v + 3;142*ind++ = v + 0;143*ind++ = v + 2;144145vert += 4;146v += 4;147}148149std::swap(vertices, newVerts);150std::swap(indices, newInds);151count *= 3;152}153154u32 CGEDebugger::PrimPreviewOp() {155DisplayList list;156if (gpuDebug != nullptr && gpuDebug->GetCurrentDisplayList(list) && !showClut_) {157const u32 op = Memory::Read_U32(list.pc);158const u32 cmd = op >> 24;159if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE) {160return op;161}162}163164return 0;165}166167static void ExpandBezier(int &count, int op, const std::vector<SimpleVertex> &simpleVerts, const std::vector<u16> &indices, std::vector<SimpleVertex> &generatedVerts, std::vector<u16> &generatedInds) {168using namespace Spline;169170int count_u = (op >> 0) & 0xFF;171int count_v = (op >> 8) & 0xFF;172// Real hardware seems to draw nothing when given < 4 either U or V.173if (count_u < 4 || count_v < 4)174return;175176BezierSurface surface;177surface.num_points_u = count_u;178surface.num_points_v = count_v;179surface.tess_u = gstate.getPatchDivisionU();180surface.tess_v = gstate.getPatchDivisionV();181surface.num_patches_u = (count_u - 1) / 3;182surface.num_patches_v = (count_v - 1) / 3;183surface.primType = gstate.getPatchPrimitiveType();184surface.patchFacing = false;185186int num_points = count_u * count_v;187// Make an array of pointers to the control points, to get rid of indices.188std::vector<const SimpleVertex *> points(num_points);189for (int idx = 0; idx < num_points; idx++)190points[idx] = simpleVerts.data() + (!indices.empty() ? indices[idx] : idx);191192int total_patches = surface.num_patches_u * surface.num_patches_v;193generatedVerts.resize((surface.tess_u + 1) * (surface.tess_v + 1) * total_patches);194generatedInds.resize(surface.tess_u * surface.tess_v * 6 * total_patches);195196OutputBuffers output;197output.vertices = generatedVerts.data();198output.indices = generatedInds.data();199output.count = 0;200201ControlPoints cpoints;202cpoints.pos = (Vec3f *)AllocateAlignedMemory(sizeof(Vec3f) * num_points, 16);203cpoints.tex = (Vec2f *)AllocateAlignedMemory(sizeof(Vec2f) * num_points, 16);204cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * num_points, 16);205cpoints.Convert(points.data(), num_points);206207surface.Init((int)generatedVerts.size());208SoftwareTessellation(output, surface, gstate.vertType, cpoints);209count = output.count;210211FreeAlignedMemory(cpoints.pos);212FreeAlignedMemory(cpoints.tex);213FreeAlignedMemory(cpoints.col);214}215216static void ExpandSpline(int &count, int op, const std::vector<SimpleVertex> &simpleVerts, const std::vector<u16> &indices, std::vector<SimpleVertex> &generatedVerts, std::vector<u16> &generatedInds) {217using namespace Spline;218219int count_u = (op >> 0) & 0xFF;220int count_v = (op >> 8) & 0xFF;221// Real hardware seems to draw nothing when given < 4 either U or V.222if (count_u < 4 || count_v < 4)223return;224225SplineSurface surface;226surface.num_points_u = count_u;227surface.num_points_v = count_v;228surface.tess_u = gstate.getPatchDivisionU();229surface.tess_v = gstate.getPatchDivisionV();230surface.type_u = (op >> 16) & 0x3;231surface.type_v = (op >> 18) & 0x3;232surface.num_patches_u = count_u - 3;233surface.num_patches_v = count_v - 3;234surface.primType = gstate.getPatchPrimitiveType();235surface.patchFacing = false;236237int num_points = count_u * count_v;238// Make an array of pointers to the control points, to get rid of indices.239std::vector<const SimpleVertex *> points(num_points);240for (int idx = 0; idx < num_points; idx++)241points[idx] = simpleVerts.data() + (!indices.empty() ? indices[idx] : idx);242243int patch_div_s = surface.num_patches_u * surface.tess_u;244int patch_div_t = surface.num_patches_v * surface.tess_v;245generatedVerts.resize((patch_div_s + 1) * (patch_div_t + 1));246generatedInds.resize(patch_div_s * patch_div_t * 6);247248OutputBuffers output;249output.vertices = generatedVerts.data();250output.indices = generatedInds.data();251output.count = 0;252253ControlPoints cpoints;254cpoints.pos = (Vec3f *)AllocateAlignedMemory(sizeof(Vec3f) * num_points, 16);255cpoints.tex = (Vec2f *)AllocateAlignedMemory(sizeof(Vec2f) * num_points, 16);256cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * num_points, 16);257cpoints.Convert(points.data(), num_points);258259surface.Init((int)generatedVerts.size());260SoftwareTessellation(output, surface, gstate.vertType, cpoints);261count = output.count;262263FreeAlignedMemory(cpoints.pos);264FreeAlignedMemory(cpoints.tex);265FreeAlignedMemory(cpoints.col);266}267268void CGEDebugger::UpdatePrimPreview(u32 op, int which) {269u32 prim_type = GE_PRIM_INVALID;270int count = 0;271int count_u = 0;272int count_v = 0;273274const u32 cmd = op >> 24;275if (cmd == GE_CMD_PRIM) {276prim_type = (op >> 16) & 0x7;277count = op & 0xFFFF;278} else {279const GEPrimitiveType primLookup[] = { GE_PRIM_TRIANGLES, GE_PRIM_LINES, GE_PRIM_POINTS, GE_PRIM_POINTS };280if (gstate.getPatchPrimitiveType() < ARRAY_SIZE(primLookup))281prim_type = primLookup[gstate.getPatchPrimitiveType()];282count_u = (op & 0x00FF) >> 0;283count_v = (op & 0xFF00) >> 8;284count = count_u * count_v;285}286287if (prim_type >= 7) {288ERROR_LOG(Log::G3D, "Unsupported prim type: %x", op);289return;290}291if (!gpuDebug) {292ERROR_LOG(Log::G3D, "Invalid debugging environment, shutting down?");293return;294}295which &= previewsEnabled_;296if (count == 0 || which == 0) {297return;298}299300const GEPrimitiveType prim = static_cast<GEPrimitiveType>(prim_type);301static std::vector<GPUDebugVertex> vertices;302static std::vector<u16> indices;303304if (!gpuDebug->GetCurrentSimpleVertices(count, vertices, indices)) {305ERROR_LOG(Log::G3D, "Vertex preview not yet supported");306return;307}308309if (cmd != GE_CMD_PRIM) {310static std::vector<SimpleVertex> generatedVerts;311static std::vector<u16> generatedInds;312313static std::vector<SimpleVertex> simpleVerts;314simpleVerts.resize(vertices.size());315for (size_t i = 0; i < vertices.size(); ++i) {316// For now, let's just copy back so we can use TessellateBezierPatch/TessellateSplinePatch...317simpleVerts[i].uv[0] = vertices[i].u;318simpleVerts[i].uv[1] = vertices[i].v;319simpleVerts[i].pos = Vec3Packedf(vertices[i].x, vertices[i].y, vertices[i].z);320}321322if (cmd == GE_CMD_BEZIER) {323ExpandBezier(count, op, simpleVerts, indices, generatedVerts, generatedInds);324} else if (cmd == GE_CMD_SPLINE) {325ExpandSpline(count, op, simpleVerts, indices, generatedVerts, generatedInds);326}327328vertices.resize(generatedVerts.size());329for (size_t i = 0; i < vertices.size(); ++i) {330vertices[i].u = generatedVerts[i].uv[0];331vertices[i].v = generatedVerts[i].uv[1];332vertices[i].x = generatedVerts[i].pos.x;333vertices[i].y = generatedVerts[i].pos.y;334vertices[i].z = generatedVerts[i].pos.z;335}336indices = generatedInds;337}338339if (prim == GE_PRIM_RECTANGLES) {340ExpandRectangles(vertices, indices, count, gpuDebug->GetGState().isModeThrough());341}342343float fw, fh;344float x, y;345346// TODO: Probably there's a better way and place to do this.347u16 minIndex = 0;348u16 maxIndex = count - 1;349if (!indices.empty()) {350_dbg_assert_(count <= indices.size());351minIndex = 0xFFFF;352maxIndex = 0;353for (int i = 0; i < count; ++i) {354if (minIndex > indices[i]) {355minIndex = indices[i];356}357if (maxIndex < indices[i]) {358maxIndex = indices[i];359}360}361}362363auto wrapCoord = [](float &coord) {364if (coord < 0.0f) {365coord += ceilf(-coord);366}367if (coord > 1.0f) {368coord -= floorf(coord);369}370};371372const float invTexWidth = 1.0f / gpuDebug->GetGState().getTextureWidth(0);373const float invTexHeight = 1.0f / gpuDebug->GetGState().getTextureHeight(0);374const float invRealTexWidth = 1.0f / gstate_c.curTextureWidth;375const float invRealTexHeight = 1.0f / gstate_c.curTextureHeight;376bool clampS = gpuDebug->GetGState().isTexCoordClampedS();377bool clampT = gpuDebug->GetGState().isTexCoordClampedT();378for (u16 i = minIndex; i <= maxIndex; ++i) {379vertices[i].u *= invTexWidth;380vertices[i].v *= invTexHeight;381if (!clampS)382wrapCoord(vertices[i].u);383if (!clampT)384wrapCoord(vertices[i].v);385}386387if (which & 1) {388primaryWindow->Begin();389primaryWindow->GetContentSize(x, y, fw, fh);390391glEnable(GL_BLEND);392glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);393glBlendEquation(GL_FUNC_ADD);394glBindTexture(GL_TEXTURE_2D, 0);395// The surface is upside down, so vertical offsets are negated.396glViewport((GLint)x, (GLint)-(y + fh - primaryWindow->Height()), (GLsizei)fw, (GLsizei)fh);397glScissor((GLint)x, (GLint)-(y + fh - primaryWindow->Height()), (GLsizei)fw, (GLsizei)fh);398BindPreviewProgram(previewProgram);399400if (previewVao == 0 && gl_extensions.ARB_vertex_array_object) {401glGenVertexArrays(1, &previewVao);402glBindVertexArray(previewVao);403glEnableVertexAttribArray(previewProgram->a_position);404405if (ibuf == 0)406glGenBuffers(1, &ibuf);407if (vbuf == 0)408glGenBuffers(1, &vbuf);409glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf);410glBindBuffer(GL_ARRAY_BUFFER, vbuf);411412glVertexAttribPointer(previewProgram->a_position, 3, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (void *)(2 * sizeof(float)));413}414415if (vbuf != 0) {416glBindBuffer(GL_ARRAY_BUFFER, vbuf);417glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GPUDebugVertex), vertices.data(), GL_STREAM_DRAW);418}419420if (ibuf != 0 && !indices.empty()) {421glBindVertexArray(previewVao);422glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(u16), indices.data(), GL_STREAM_DRAW);423}424425float scale[] = {426480.0f / (float)PSP_CoreParameter().renderWidth,427272.0f / (float)PSP_CoreParameter().renderHeight,428};429430Lin::Matrix4x4 ortho;431ortho.setOrtho(-(float)gstate_c.curRTOffsetX, (primaryWindow->TexWidth() - (int)gstate_c.curRTOffsetX) * scale[0], primaryWindow->TexHeight() * scale[1], 0, -1, 1);432glUniformMatrix4fv(previewProgram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr());433if (previewVao != 0) {434glBindVertexArray(previewVao);435} else {436glEnableVertexAttribArray(previewProgram->a_position);437glVertexAttribPointer(previewProgram->a_position, 3, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (float *)vertices.data() + 2);438}439440if (indices.empty()) {441glDrawArrays(glprim[prim], 0, count);442} else {443glDrawElements(glprim[prim], count, GL_UNSIGNED_SHORT, previewVao != 0 ? 0 : indices.data());444}445446if (previewVao == 0) {447glDisableVertexAttribArray(previewProgram->a_position);448}449450primaryWindow->End();451}452453if (which & 2) {454secondWindow->Begin();455secondWindow->GetContentSize(x, y, fw, fh);456457glEnable(GL_BLEND);458glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);459glBlendEquation(GL_FUNC_ADD);460glBindTexture(GL_TEXTURE_2D, 0);461// The surface is upside down, so vertical offsets are flipped.462glViewport((GLint)x, (GLint)-(y + fh - secondWindow->Height()), (GLsizei)fw, (GLsizei)fh);463glScissor((GLint)x, (GLint)-(y + fh - secondWindow->Height()), (GLsizei)fw, (GLsizei)fh);464BindPreviewProgram(texPreviewProgram);465466if (texPreviewVao == 0 && vbuf != 0 && ibuf != 0 && gl_extensions.ARB_vertex_array_object) {467glGenVertexArrays(1, &texPreviewVao);468glBindVertexArray(texPreviewVao);469glEnableVertexAttribArray(texPreviewProgram->a_position);470471if (ibuf == 0)472glGenBuffers(1, &ibuf);473if (vbuf == 0)474glGenBuffers(1, &vbuf);475glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf);476glBindBuffer(GL_ARRAY_BUFFER, vbuf);477478glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), 0);479}480481// TODO: For some reason we have to re-upload the data?482if (vbuf != 0) {483glBindBuffer(GL_ARRAY_BUFFER, vbuf);484glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GPUDebugVertex), vertices.data(), GL_STREAM_DRAW);485}486487if (ibuf != 0 && !indices.empty()) {488glBindVertexArray(texPreviewVao);489glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(u16), indices.data(), GL_STREAM_DRAW);490}491492Lin::Matrix4x4 ortho;493ortho.setOrtho(0.0f - (float)gstate_c.curTextureXOffset * invRealTexWidth, 1.0f - (float)gstate_c.curTextureXOffset * invRealTexWidth, 1.0f - (float)gstate_c.curTextureYOffset * invRealTexHeight, 0.0f - (float)gstate_c.curTextureYOffset * invRealTexHeight, -1.0f, 1.0f);494glUniformMatrix4fv(texPreviewProgram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr());495if (texPreviewVao != 0) {496glBindVertexArray(texPreviewVao);497glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf);498glBindBuffer(GL_ARRAY_BUFFER, vbuf);499glEnableVertexAttribArray(texPreviewProgram->a_position);500glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), 0);501} else {502glEnableVertexAttribArray(texPreviewProgram->a_position);503glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (float *)vertices.data());504}505506if (indices.empty()) {507glDrawArrays(glprim[prim], 0, count);508} else {509glDrawElements(glprim[prim], count, GL_UNSIGNED_SHORT, texPreviewVao != 0 ? 0 : indices.data());510}511512if (texPreviewVao == 0) {513glDisableVertexAttribArray(texPreviewProgram->a_position);514}515516secondWindow->End();517}518}519520void CGEDebugger::CleanupPrimPreview() {521if (previewProgram) {522glsl_destroy(previewProgram);523}524if (texPreviewProgram) {525glsl_destroy(texPreviewProgram);526}527}528529void CGEDebugger::HandleRedraw(int which) {530if (updating_) {531return;532}533534u32 op = PrimPreviewOp();535if (op) {536UpdatePrimPreview(op, which);537}538}539540541