mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-22 21:09:52 +00:00
Add new shader viewing tool
This commit is contained in:
parent
09cc80f413
commit
7f4e473e8c
@ -863,6 +863,7 @@ set(NativeAppSource
|
||||
UI/Store.cpp
|
||||
UI/CwCheatScreen.cpp
|
||||
UI/InstallZipScreen.cpp
|
||||
UI/ProfilerDraw.cpp
|
||||
UI/ui_atlas.cpp)
|
||||
if(ANDROID)
|
||||
if (ARM)
|
||||
|
@ -323,6 +323,7 @@ static ConfigSetting generalSettings[] = {
|
||||
#endif
|
||||
ConfigSetting("PauseWhenMinimized", &g_Config.bPauseWhenMinimized, false, true, true),
|
||||
ConfigSetting("DumpDecryptedEboots", &g_Config.bDumpDecryptedEboot, false, true, true),
|
||||
ConfigSetting("FullscreenOnDoubleclick", &g_Config.bFullscreenOnDoubleclick, true, false, false),
|
||||
ConfigSetting(false),
|
||||
};
|
||||
|
||||
|
@ -90,12 +90,12 @@ public:
|
||||
bool bScreenshotsAsPNG;
|
||||
bool bEnableLogging;
|
||||
bool bDumpDecryptedEboot;
|
||||
bool bFullscreenOnDoubleclick;
|
||||
#if defined(USING_WIN_UI)
|
||||
bool bPauseOnLostFocus;
|
||||
bool bTopMost;
|
||||
std::string sFont;
|
||||
bool bIgnoreWindowsKey;
|
||||
|
||||
// Used for switching the GPU backend in GameSettingsScreen.
|
||||
// Without this, PPSSPP instantly crashes if we edit iGPUBackend directly...
|
||||
int iTempGPUBackend;
|
||||
|
29
GPU/Common/ShaderCommon.h
Normal file
29
GPU/Common/ShaderCommon.h
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2015- 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/.
|
||||
|
||||
#pragma once
|
||||
|
||||
enum DebugShaderType {
|
||||
SHADER_TYPE_VERTEX = 0,
|
||||
SHADER_TYPE_FRAGMENT = 1,
|
||||
};
|
||||
|
||||
enum DebugShaderStringType {
|
||||
SHADER_STRING_SHORT_DESC = 0,
|
||||
SHADER_STRING_SOURCE_CODE = 1,
|
||||
SHADER_STRING_STATS = 2,
|
||||
};
|
@ -24,6 +24,7 @@
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "gfx_es2/gpu_features.h"
|
||||
@ -330,6 +331,38 @@ enum {
|
||||
BIT_FLATSHADE = 46,
|
||||
};
|
||||
|
||||
std::string FragmentShaderDesc(const ShaderID &id) {
|
||||
std::stringstream desc;
|
||||
if (id.Bit(BIT_CLEARMODE)) desc << "Clear ";
|
||||
if (id.Bit(BIT_DO_TEXTURE)) desc << "Tex ";
|
||||
if (id.Bit(BIT_DO_TEXTURE_PROJ)) desc << "TexProj ";
|
||||
if (id.Bit(BIT_FLIP_TEXTURE)) desc << "Flip ";
|
||||
if (id.Bit(BIT_TEXALPHA)) desc << "TexAlpha ";
|
||||
if (id.Bit(BIT_TEXTURE_AT_OFFSET)) desc << "TexOffset ";
|
||||
if (id.Bit(BIT_LMODE)) desc << "LM ";
|
||||
if (id.Bit(BIT_ENABLE_FOG)) desc << "Fog ";
|
||||
if (id.Bit(BIT_COLOR_DOUBLE)) desc << "Double ";
|
||||
if (id.Bit(BIT_FLATSHADE)) desc << "Flat ";
|
||||
if (id.Bit(BIT_SHADER_TEX_CLAMP)) desc << "Texclamp";
|
||||
if (id.Bit(BIT_CLAMP_S)) desc << "Clamp S ";
|
||||
if (id.Bit(BIT_CLAMP_T)) desc << "Clamp T ";
|
||||
|
||||
switch (id.Bits(BIT_STENCIL_TO_ALPHA, 2)) {
|
||||
case REPLACE_ALPHA_NO: break;
|
||||
case REPLACE_ALPHA_YES: desc << "StencilToAlpha "; break;
|
||||
case REPLACE_ALPHA_DUALSOURCE: desc << "StencilToAlphaDualSrc "; break;
|
||||
}
|
||||
|
||||
if (id.Bit(BIT_ALPHA_TEST)) desc << "AlphaTest ";
|
||||
if (id.Bit(BIT_ALPHA_AGAINST_ZERO)) desc << "AlphaTest0 ";
|
||||
if (id.Bit(BIT_COLOR_TEST)) desc << "ColorTest ";
|
||||
if (id.Bit(BIT_COLOR_AGAINST_ZERO)) desc << "ColorTest0 ";
|
||||
|
||||
// TODO: A few more...
|
||||
|
||||
return desc.str();
|
||||
}
|
||||
|
||||
// Here we must take all the bits of the gstate that determine what the fragment shader will
|
||||
// look like, and concatenate them together into an ID.
|
||||
void ComputeFragmentShaderID(ShaderID *id_out, uint32_t vertType) {
|
||||
|
@ -23,6 +23,7 @@ struct ShaderID;
|
||||
|
||||
void ComputeFragmentShaderID(ShaderID *id, uint32_t vertType);
|
||||
bool GenerateFragmentShader(const ShaderID &id, char *buffer);
|
||||
std::string FragmentShaderDesc(const ShaderID &id);
|
||||
|
||||
enum StencilValueType {
|
||||
STENCIL_VALUE_UNIFORM,
|
||||
|
@ -2411,3 +2411,10 @@ bool GLES_GPU::DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> GLES_GPU::DebugGetShaderIDs(DebugShaderType type) {
|
||||
return shaderManager_->DebugGetShaderIDs(type);
|
||||
}
|
||||
std::string GLES_GPU::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {
|
||||
return shaderManager_->DebugGetShaderString(id, type, stringType);
|
||||
}
|
||||
|
@ -150,6 +150,10 @@ public:
|
||||
void Execute_BoneMtxData(u32 op, u32 diff);
|
||||
void Execute_BlockTransferStart(u32 op, u32 diff);
|
||||
|
||||
// Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend.
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) override;
|
||||
std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override;
|
||||
|
||||
protected:
|
||||
void FastRunLoop(DisplayList &list) override;
|
||||
void ProcessEvent(GPUEvent ev) override;
|
||||
|
@ -41,9 +41,10 @@
|
||||
#include "Framebuffer.h"
|
||||
#include "i18n/i18n.h"
|
||||
|
||||
Shader::Shader(const char *code, uint32_t shaderType, bool useHWTransform, const ShaderID &shaderID)
|
||||
Shader::Shader(const char *code, uint32_t glShaderType, bool useHWTransform, const ShaderID &shaderID)
|
||||
: id_(shaderID), failed_(false), useHWTransform_(useHWTransform) {
|
||||
PROFILE_THIS_SCOPE("shadercomp");
|
||||
isFragment_ = glShaderType == GL_FRAGMENT_SHADER;
|
||||
source_ = code;
|
||||
#ifdef SHADERLOG
|
||||
#ifdef _WIN32
|
||||
@ -52,7 +53,7 @@ Shader::Shader(const char *code, uint32_t shaderType, bool useHWTransform, const
|
||||
printf("%s\n", code);
|
||||
#endif
|
||||
#endif
|
||||
shader = glCreateShader(shaderType);
|
||||
shader = glCreateShader(glShaderType);
|
||||
glShaderSource(shader, 1, &code, 0);
|
||||
glCompileShader(shader);
|
||||
GLint success = 0;
|
||||
@ -130,13 +131,17 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans
|
||||
ELOG("Could not link program:\n %s", buf);
|
||||
#endif
|
||||
ERROR_LOG(G3D, "Could not link program:\n %s", buf);
|
||||
ERROR_LOG(G3D, "VS:\n%s", vs->source().c_str());
|
||||
ERROR_LOG(G3D, "FS:\n%s", fs->source().c_str());
|
||||
Reporting::ReportMessage("Error in shader program link: info: %s / fs: %s / vs: %s", buf, fs->source().c_str(), vs->source().c_str());
|
||||
ERROR_LOG(G3D, "VS desc:\n%s\n", vs->GetShaderString(SHADER_STRING_SHORT_DESC).c_str());
|
||||
ERROR_LOG(G3D, "FS desc:\n%s\n", fs->GetShaderString(SHADER_STRING_SHORT_DESC).c_str());
|
||||
std::string vs_source = vs->GetShaderString(SHADER_STRING_SOURCE_CODE);
|
||||
std::string fs_source = fs->GetShaderString(SHADER_STRING_SOURCE_CODE);
|
||||
ERROR_LOG(G3D, "VS:\n%s\n", vs_source.c_str());
|
||||
ERROR_LOG(G3D, "FS:\n%s\n", fs_source.c_str());
|
||||
Reporting::ReportMessage("Error in shader program link: info: %s / fs: %s / vs: %s", buf, fs_source.c_str(), vs_source.c_str());
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringUTF8(buf);
|
||||
OutputDebugStringUTF8(vs->source().c_str());
|
||||
OutputDebugStringUTF8(fs->source().c_str());
|
||||
OutputDebugStringUTF8(vs_source.c_str());
|
||||
OutputDebugStringUTF8(fs_source.c_str());
|
||||
#endif
|
||||
delete [] buf; // we're dead!
|
||||
}
|
||||
@ -879,3 +884,64 @@ LinkedShader *ShaderManager::ApplyFragmentShader(Shader *vs, int prim, u32 vertT
|
||||
lastShader_ = ls;
|
||||
return ls;
|
||||
}
|
||||
|
||||
std::string Shader::GetShaderString(DebugShaderStringType type) const {
|
||||
switch (type) {
|
||||
case SHADER_STRING_SOURCE_CODE:
|
||||
return source_;
|
||||
case SHADER_STRING_SHORT_DESC:
|
||||
return isFragment_ ? FragmentShaderDesc(id_) : VertexShaderDesc(id_);
|
||||
default:
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> ShaderManager::DebugGetShaderIDs(DebugShaderType type) {
|
||||
std::string id;
|
||||
std::vector<std::string> ids;
|
||||
switch (type) {
|
||||
case SHADER_TYPE_VERTEX:
|
||||
{
|
||||
for (auto iter : vsCache_) {
|
||||
iter.first.ToString(&id);
|
||||
ids.push_back(id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SHADER_TYPE_FRAGMENT:
|
||||
{
|
||||
for (auto iter : fsCache_) {
|
||||
iter.first.ToString(&id);
|
||||
ids.push_back(id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
std::string ShaderManager::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {
|
||||
ShaderID shaderId;
|
||||
shaderId.FromString(id);
|
||||
switch (type) {
|
||||
case SHADER_TYPE_VERTEX:
|
||||
{
|
||||
auto iter = vsCache_.find(shaderId);
|
||||
if (iter == vsCache_.end()) {
|
||||
return "";
|
||||
}
|
||||
return iter->second->GetShaderString(stringType);
|
||||
}
|
||||
|
||||
case SHADER_TYPE_FRAGMENT:
|
||||
{
|
||||
auto iter = fsCache_.find(shaderId);
|
||||
if (iter == fsCache_.end()) {
|
||||
return "";
|
||||
}
|
||||
return iter->second->GetShaderString(stringType);
|
||||
}
|
||||
default:
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,10 @@
|
||||
#include "base/basictypes.h"
|
||||
#include "Globals.h"
|
||||
#include <map>
|
||||
#include "VertexShaderGenerator.h"
|
||||
#include "FragmentShaderGenerator.h"
|
||||
|
||||
#include "GPU/Common/ShaderCommon.h"
|
||||
#include "GPU/GLES/VertexShaderGenerator.h"
|
||||
#include "GPU/GLES/FragmentShaderGenerator.h"
|
||||
|
||||
class Shader;
|
||||
|
||||
@ -30,12 +32,12 @@ struct ShaderID {
|
||||
clear();
|
||||
}
|
||||
void clear() {
|
||||
for (int i = 0; i < ARRAY_SIZE(d); i++) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(d); i++) {
|
||||
d[i] = 0;
|
||||
}
|
||||
}
|
||||
void set_invalid() {
|
||||
for (int i = 0; i < ARRAY_SIZE(d); i++) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(d); i++) {
|
||||
d[i] = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
@ -77,6 +79,14 @@ struct ShaderID {
|
||||
d[bit >> 5] |= (value & mask) << (bit & 31);
|
||||
}
|
||||
}
|
||||
|
||||
void ToString(std::string *dest) const {
|
||||
dest->resize(sizeof(d));
|
||||
memcpy(&(*dest)[0], d, sizeof(d));
|
||||
}
|
||||
void FromString(std::string src) {
|
||||
memcpy(d, &(src)[0], sizeof(d));
|
||||
}
|
||||
};
|
||||
|
||||
// Pre-fetched attrs and uniforms
|
||||
@ -213,20 +223,22 @@ enum {
|
||||
|
||||
class Shader {
|
||||
public:
|
||||
Shader(const char *code, uint32_t shaderType, bool useHWTransform, const ShaderID &shaderID);
|
||||
Shader(const char *code, uint32_t glShaderType, bool useHWTransform, const ShaderID &shaderID);
|
||||
~Shader();
|
||||
uint32_t shader;
|
||||
const std::string &source() const { return source_; }
|
||||
|
||||
bool Failed() const { return failed_; }
|
||||
bool UseHWTransform() const { return useHWTransform_; }
|
||||
const ShaderID &ID() const { return id_; }
|
||||
|
||||
std::string GetShaderString(DebugShaderStringType type) const;
|
||||
|
||||
private:
|
||||
std::string source_;
|
||||
ShaderID id_;
|
||||
bool failed_;
|
||||
bool useHWTransform_;
|
||||
bool isFragment_;
|
||||
};
|
||||
|
||||
class ShaderManager {
|
||||
@ -251,6 +263,9 @@ public:
|
||||
int NumFragmentShaders() const { return (int)fsCache_.size(); }
|
||||
int NumPrograms() const { return (int)linkedShaderCache_.size(); }
|
||||
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type);
|
||||
std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType);
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
static bool DebugAreShadersCompatibleForLinking(Shader *vs, Shader *fs);
|
||||
|
@ -43,9 +43,8 @@
|
||||
|
||||
#define WRITE p+=sprintf
|
||||
|
||||
// These bits are internal to this file, although the resulting IDs will be externally visible.
|
||||
// TODO: We will cut away many of these, turning them into uniforms. This should reduce the number
|
||||
// of generated shaders drastically.
|
||||
// TODO: There will be additional bits, indicating that groups of these will be
|
||||
// sent to the shader and processed there. This will cut down the number of shaders ("ubershader approach")
|
||||
enum {
|
||||
BIT_LMODE = 0,
|
||||
BIT_IS_THROUGH = 1,
|
||||
@ -57,32 +56,71 @@ enum {
|
||||
BIT_USE_HW_TRANSFORM = 8,
|
||||
BIT_HAS_NORMAL = 9, // conditioned on hw transform
|
||||
BIT_NORM_REVERSE = 10,
|
||||
BIT_HAS_TEXCOORD = 11,
|
||||
BIT_HAS_TEXCOORD = 11, // 5 free after
|
||||
BIT_UVGEN_MODE = 16,
|
||||
BIT_UVPROJ_MODE = 18, // 2, can overlap with LS0
|
||||
BIT_LS0 = 18, // 2
|
||||
BIT_LS1 = 20, // 2
|
||||
BIT_BONES = 22, // 3 should be enough, not 8
|
||||
BIT_ENABLE_BONES = 30,
|
||||
BIT_LIGHT0_COMP = 32,
|
||||
BIT_LIGHT0_TYPE = 34,
|
||||
BIT_LIGHT1_COMP = 36,
|
||||
BIT_LIGHT1_TYPE = 38,
|
||||
BIT_LIGHT2_COMP = 40,
|
||||
BIT_LIGHT2_TYPE = 42,
|
||||
BIT_LIGHT3_COMP = 44,
|
||||
BIT_LIGHT3_TYPE = 46,
|
||||
BIT_MATERIAL_UPDATE = 48,
|
||||
BIT_LIGHT0_COMP = 32, // 2 bits
|
||||
BIT_LIGHT0_TYPE = 34, // 2 bits
|
||||
BIT_LIGHT1_COMP = 36, // 2 bits
|
||||
BIT_LIGHT1_TYPE = 38, // 2 bits
|
||||
BIT_LIGHT2_COMP = 40, // 2 bits
|
||||
BIT_LIGHT2_TYPE = 42, // 2 bits
|
||||
BIT_LIGHT3_COMP = 44, // 2 bits
|
||||
BIT_LIGHT3_TYPE = 46, // 2 bits
|
||||
BIT_MATERIAL_UPDATE = 48, // 3 bits, 1 free after
|
||||
BIT_LIGHT0_ENABLE = 52,
|
||||
BIT_LIGHT1_ENABLE = 53,
|
||||
BIT_LIGHT2_ENABLE = 54,
|
||||
BIT_LIGHT3_ENABLE = 55,
|
||||
BIT_LIGHTING_ENABLE = 56,
|
||||
BIT_WEIGHT_FMTSCALE = 57,
|
||||
BIT_WEIGHT_FMTSCALE = 57, // only two bits, 1 free after
|
||||
BIT_TEXCOORD_FMTSCALE = 60,
|
||||
BIT_FLATSHADE = 62,
|
||||
BIT_FLATSHADE = 62, // 1 free after
|
||||
};
|
||||
|
||||
std::string VertexShaderDesc(const ShaderID &id) {
|
||||
std::stringstream desc;
|
||||
if (id.Bit(BIT_IS_THROUGH)) desc << "THR ";
|
||||
if (id.Bit(BIT_USE_HW_TRANSFORM)) desc << "HWX ";
|
||||
if (id.Bit(BIT_HAS_COLOR)) desc << "C ";
|
||||
if (id.Bit(BIT_HAS_TEXCOORD)) desc << "T ";
|
||||
if (id.Bit(BIT_HAS_NORMAL)) desc << "N ";
|
||||
if (id.Bit(BIT_LMODE)) desc << "LM ";
|
||||
if (id.Bit(BIT_ENABLE_FOG)) desc << "Fog ";
|
||||
if (id.Bit(BIT_NORM_REVERSE)) desc << "RevN ";
|
||||
if (id.Bit(BIT_DO_TEXTURE)) desc << "Tex ";
|
||||
if (id.Bit(BIT_DO_TEXTURE_PROJ)) desc << "TexProj ";
|
||||
if (id.Bit(BIT_FLIP_TEXTURE)) desc << "Flip ";
|
||||
int uvgMode = id.Bits(BIT_UVGEN_MODE, 2);
|
||||
const char *uvgModes[4] = { "UV ", "UVMtx ", "UVEnv ", "UVUnk " };
|
||||
|
||||
if (uvgMode) desc << uvgModes[uvgMode];
|
||||
if (id.Bit(BIT_ENABLE_BONES)) desc << "Bones:" << (id.Bits(BIT_BONES, 3) + 1) << " ";
|
||||
// Lights
|
||||
if (id.Bit(BIT_LIGHTING_ENABLE)) {
|
||||
desc << "Light: ";
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (id.Bit(BIT_LIGHT0_ENABLE + i)) {
|
||||
desc << i << ": ";
|
||||
desc << "c:" << id.Bits(BIT_LIGHT0_COMP + 4 * i, 2) << " t:" << id.Bits(BIT_LIGHT0_TYPE + 4 * i, 2) << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
desc << "MatUp:" << id.Bits(BIT_MATERIAL_UPDATE, 3) << " ";
|
||||
|
||||
desc << "WScale " << id.Bits(BIT_WEIGHT_FMTSCALE, 2) << " ";
|
||||
desc << "TCScale " << id.Bits(BIT_TEXCOORD_FMTSCALE, 2) << " ";
|
||||
|
||||
if (id.Bit(BIT_FLATSHADE)) desc << "Flat ";
|
||||
|
||||
// TODO: More...
|
||||
return desc.str();
|
||||
}
|
||||
|
||||
bool CanUseHardwareTransform(int prim) {
|
||||
if (!g_Config.bHardwareTransform)
|
||||
return false;
|
||||
@ -118,7 +156,7 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform)
|
||||
id.SetBit(BIT_USE_HW_TRANSFORM);
|
||||
id.SetBit(BIT_HAS_NORMAL, hasNormal);
|
||||
|
||||
// UV generation mode.
|
||||
// UV generation mode. doShadeMapping is implicitly stored here.
|
||||
id.SetBits(BIT_UVGEN_MODE, 2, gstate.getUVGenMode());
|
||||
|
||||
// The next bits are used differently depending on UVgen mode
|
||||
@ -138,20 +176,17 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform)
|
||||
|
||||
// Okay, d[1] coming up. ==============
|
||||
if (gstate.isLightingEnabled() || doShadeMapping) {
|
||||
// doShadeMapping is stored as UVGenMode, so this is enough for isLightingEnabled.
|
||||
id.SetBit(BIT_LIGHTING_ENABLE);
|
||||
// Light bits
|
||||
for (int i = 0; i < 4; i++) {
|
||||
id.SetBit(BIT_LIGHT0_ENABLE + i, gstate.isLightChanEnabled(i) != 0);
|
||||
if (gstate.isLightChanEnabled(i) || (doShadeMapping && (gstate.getUVLS0() == i || gstate.getUVLS1() == i))) {
|
||||
id.SetBits(BIT_LIGHT0_COMP + 4 * i, 2, gstate.getLightComputation(i));
|
||||
id.SetBits(BIT_LIGHT0_TYPE + 4 * i, 2, gstate.getLightType(i));
|
||||
}
|
||||
}
|
||||
id.SetBits(BIT_MATERIAL_UPDATE, 3, gstate.getMaterialUpdate() & 7);
|
||||
// TODO: Optimize by shifting in all the light bits together.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
id.SetBit(BIT_LIGHT0_ENABLE + i, gstate.isLightChanEnabled(i) != 0);
|
||||
}
|
||||
// doShadeMapping is stored as UVGenMode, so this is enough for isLightingEnabled.
|
||||
id.SetBit(BIT_LIGHTING_ENABLE);
|
||||
}
|
||||
|
||||
// 2 bits. We should probably send in the weight scalefactor as a uniform instead,
|
||||
@ -159,7 +194,7 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform)
|
||||
id.SetBits(BIT_WEIGHT_FMTSCALE, 2, vertTypeGetWeightMask(vertType));
|
||||
id.SetBit(BIT_NORM_REVERSE, gstate.areNormalsReversed());
|
||||
if (doTextureProjection && gstate.getUVProjMode() == GE_PROJMAP_UV) {
|
||||
id.SetBit(BIT_TEXCOORD_FMTSCALE, (vertType & GE_VTYPE_TC_MASK) >> GE_VTYPE_TC_SHIFT); // two bits
|
||||
id.SetBits(BIT_TEXCOORD_FMTSCALE, 2, (vertType & GE_VTYPE_TC_MASK) >> GE_VTYPE_TC_SHIFT); // two bits
|
||||
} else {
|
||||
id.SetBit(BIT_HAS_TEXCOORD, hasTexcoord);
|
||||
}
|
||||
@ -406,7 +441,6 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) {
|
||||
WRITE(p, "uniform lowp vec4 u_ambient;\n");
|
||||
if ((matUpdate & 2) == 0 || !hasColor)
|
||||
WRITE(p, "uniform lowp vec3 u_matdiffuse;\n");
|
||||
// if ((gstate.materialupdate & 4) == 0)
|
||||
WRITE(p, "uniform lowp vec4 u_matspecular;\n"); // Specular coef is contained in alpha
|
||||
WRITE(p, "uniform lowp vec3 u_matemissive;\n");
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
// #define USE_BONE_ARRAY
|
||||
|
||||
@ -27,3 +27,7 @@ bool CanUseHardwareTransform(int prim);
|
||||
|
||||
void ComputeVertexShaderID(ShaderID *id, u32 vertexType, bool useHWTransform);
|
||||
void GenerateVertexShader(const ShaderID &id, char *buffer);
|
||||
|
||||
// Generates a compact string that describes the shader. Useful in a list to get an overview
|
||||
// of the current flora of shaders.
|
||||
std::string VertexShaderDesc(const ShaderID &id);
|
||||
|
@ -186,6 +186,7 @@
|
||||
<ClInclude Include="Common\GPUStateUtils.h" />
|
||||
<ClInclude Include="Common\IndexGenerator.h" />
|
||||
<ClInclude Include="Common\PostShader.h" />
|
||||
<ClInclude Include="Common\ShaderCommon.h" />
|
||||
<ClInclude Include="Common\SoftwareTransformCommon.h" />
|
||||
<ClInclude Include="Common\SplineCommon.h" />
|
||||
<ClInclude Include="Common\TextureDecoderNEON.h">
|
||||
|
@ -195,6 +195,9 @@
|
||||
<ClInclude Include="Common\GPUStateUtils.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\ShaderCommon.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Math3D.cpp">
|
||||
@ -375,4 +378,4 @@
|
||||
<Filter>GLES</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -120,6 +120,11 @@ public:
|
||||
virtual void ClearShaderCache() {}
|
||||
virtual void CleanupBeforeUI() {}
|
||||
|
||||
std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) override { return std::vector<std::string>(); };
|
||||
std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override {
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
protected:
|
||||
// To avoid virtual calls to PreExecuteOp().
|
||||
virtual void FastRunLoop(DisplayList &list) = 0;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "GPU/GPU.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/Common/ShaderCommon.h"
|
||||
|
||||
struct PspGeListArgs;
|
||||
struct GPUgstate;
|
||||
@ -285,4 +286,8 @@ public:
|
||||
virtual const std::list<int>& GetDisplayLists() = 0;
|
||||
virtual bool DecodeTexture(u8* dest, const GPUgstate &state) = 0;
|
||||
virtual std::vector<FramebufferInfo> GetFramebufferList() = 0;
|
||||
|
||||
// For debugging. The IDs returned are opaque, do not poke in them or display them in any way.
|
||||
virtual std::vector<std::string> DebugGetShaderIDs(DebugShaderType type) = 0;
|
||||
virtual std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) = 0;
|
||||
};
|
||||
|
@ -67,6 +67,7 @@ void DevMenu::CreatePopupContents(UI::ViewGroup *parent) {
|
||||
parent->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DevMenu::OnLogConfig);
|
||||
parent->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenu::OnDeveloperTools);
|
||||
parent->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenu::OnJitCompare);
|
||||
parent->Add(new Choice(dev->T("Shader Viewer")))->OnClick.Handle(this, &DevMenu::OnShaderView);
|
||||
parent->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenu::OnFreezeFrame);
|
||||
parent->Add(new Choice(dev->T("Dump Frame GPU Commands")))->OnClick.Handle(this, &DevMenu::OnDumpFrame);
|
||||
parent->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenu::OnToggleAudioDebug);
|
||||
@ -110,6 +111,14 @@ UI::EventReturn DevMenu::OnJitCompare(UI::EventParams &e) {
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
UI::EventReturn DevMenu::OnShaderView(UI::EventParams &e) {
|
||||
UpdateUIState(UISTATE_PAUSEMENU);
|
||||
screenManager()->push(new ShaderListScreen());
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
UI::EventReturn DevMenu::OnFreezeFrame(UI::EventParams &e) {
|
||||
if (PSP_CoreParameter().frozen) {
|
||||
PSP_CoreParameter().frozen = false;
|
||||
@ -761,7 +770,6 @@ void JitCompareScreen::OnRandomBlock(int flag) {
|
||||
UpdateDisasm();
|
||||
}
|
||||
|
||||
|
||||
UI::EventReturn JitCompareScreen::OnCurrentBlock(UI::EventParams &e) {
|
||||
if (!MIPSComp::jit) {
|
||||
return UI::EVENT_DONE;
|
||||
@ -778,173 +786,74 @@ UI::EventReturn JitCompareScreen::OnCurrentBlock(UI::EventParams &e) {
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
static const uint32_t nice_colors[] = {
|
||||
0xFF8040,
|
||||
0x80FF40,
|
||||
0x8040FF,
|
||||
0xFFFF40,
|
||||
|
||||
0x40FFFF,
|
||||
0xFF70FF,
|
||||
0xc0c0c0,
|
||||
0xb040c0,
|
||||
|
||||
0x184099,
|
||||
0xCC3333,
|
||||
0xFF99CC,
|
||||
0x3399CC,
|
||||
|
||||
0x990000,
|
||||
0x003366,
|
||||
0xF8F8F8,
|
||||
0x33FFFF,
|
||||
};
|
||||
|
||||
enum ProfileCatStatus {
|
||||
PROFILE_CAT_VISIBLE = 0,
|
||||
PROFILE_CAT_IGNORE = 1,
|
||||
PROFILE_CAT_NOLEGEND = 2,
|
||||
};
|
||||
|
||||
void DrawProfile(UIContext &ui) {
|
||||
#ifdef USE_PROFILER
|
||||
int numCategories = Profiler_GetNumCategories();
|
||||
int historyLength = Profiler_GetHistoryLength();
|
||||
|
||||
ui.SetFontStyle(ui.theme->uiFont);
|
||||
|
||||
static float lastMaxVal = 1.0f / 60.0f;
|
||||
float legendMinVal = lastMaxVal * (1.0f / 120.0f);
|
||||
|
||||
std::vector<float> history;
|
||||
std::vector<ProfileCatStatus> catStatus;
|
||||
history.resize(historyLength);
|
||||
catStatus.resize(numCategories);
|
||||
|
||||
float rowH = 30.0f;
|
||||
float legendHeight = 0.0f;
|
||||
float legendWidth = 80.0f;
|
||||
for (int i = 0; i < numCategories; i++) {
|
||||
const char *name = Profiler_GetCategoryName(i);
|
||||
if (!strcmp(name, "timing")) {
|
||||
catStatus[i] = PROFILE_CAT_IGNORE;
|
||||
continue;
|
||||
}
|
||||
|
||||
Profiler_GetHistory(i, &history[0], historyLength);
|
||||
catStatus[i] = PROFILE_CAT_NOLEGEND;
|
||||
for (int j = 0; j < historyLength; ++j) {
|
||||
if (history[j] > legendMinVal) {
|
||||
catStatus[i] = PROFILE_CAT_VISIBLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// So they don't move horizontally, we always measure.
|
||||
float w = 0.0f, h = 0.0f;
|
||||
ui.MeasureText(ui.GetFontStyle(), name, &w, &h);
|
||||
if (w > legendWidth) {
|
||||
legendWidth = w;
|
||||
}
|
||||
legendHeight += rowH;
|
||||
void ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout *view) {
|
||||
using namespace UI;
|
||||
std::vector<std::string> shaderIds_ = gpu->DebugGetShaderIDs(shaderType);
|
||||
for (auto id : shaderIds_) {
|
||||
Choice *choice = view->Add(new Choice(gpu->DebugGetShaderString(id, shaderType, SHADER_STRING_SHORT_DESC)));
|
||||
choice->SetTag(id);
|
||||
choice->OnClick.Handle(this, &ShaderListScreen::OnShaderClick);
|
||||
}
|
||||
legendWidth += 20.0f;
|
||||
|
||||
float legendStartY = legendHeight > ui.GetBounds().centerY() ? ui.GetBounds().y2() - legendHeight : ui.GetBounds().centerY();
|
||||
float legendStartX = ui.GetBounds().x2() - std::min(legendWidth, 200.0f);
|
||||
|
||||
const uint32_t opacity = 140 << 24;
|
||||
|
||||
int legendNum = 0;
|
||||
for (int i = 0; i < numCategories; i++) {
|
||||
const char *name = Profiler_GetCategoryName(i);
|
||||
uint32_t color = nice_colors[i % ARRAY_SIZE(nice_colors)];
|
||||
|
||||
if (catStatus[i] == PROFILE_CAT_VISIBLE) {
|
||||
float y = legendStartY + legendNum++ * rowH;
|
||||
ui.FillRect(UI::Drawable(opacity | color), Bounds(legendStartX, y, rowH - 2, rowH - 2));
|
||||
ui.DrawTextShadow(name, legendStartX + rowH + 2, y, 0xFFFFFFFF, ALIGN_VBASELINE);
|
||||
}
|
||||
}
|
||||
|
||||
float graphWidth = ui.GetBounds().x2() - legendWidth - 20.0f;
|
||||
float graphHeight = ui.GetBounds().h * 0.8f;
|
||||
|
||||
float dx = graphWidth / historyLength;
|
||||
|
||||
/*
|
||||
ui.Flush();
|
||||
|
||||
ui.BeginNoTex();
|
||||
*/
|
||||
|
||||
bool area = true;
|
||||
float minVal = 0.0f;
|
||||
float maxVal = lastMaxVal; // TODO - adjust to frame length
|
||||
if (maxVal < 0.001f)
|
||||
maxVal = 0.001f;
|
||||
if (maxVal > 1.0f / 15.0f)
|
||||
maxVal = 1.0f / 15.0f;
|
||||
|
||||
float scale = (graphHeight) / (maxVal - minVal);
|
||||
|
||||
float y_60th = ui.GetBounds().y2() - 10 - (1.0f / 60.0f) * scale;
|
||||
float y_1ms = ui.GetBounds().y2() - 10 - (1.0f / 1000.0f) * scale;
|
||||
|
||||
ui.FillRect(UI::Drawable(0x80FFFF00), Bounds(0, y_60th, graphWidth, 2));
|
||||
ui.FillRect(UI::Drawable(0x80FFFF00), Bounds(0, y_1ms, graphWidth, 2));
|
||||
ui.DrawTextShadow("1/60s", 5, y_60th, 0x80FFFF00);
|
||||
ui.DrawTextShadow("1ms", 5, y_1ms, 0x80FFFF00);
|
||||
|
||||
std::vector<float> total;
|
||||
total.resize(historyLength);
|
||||
|
||||
maxVal = 0.0f;
|
||||
float maxTotal = 0.0f;
|
||||
for (int i = 0; i < numCategories; i++) {
|
||||
if (catStatus[i] == PROFILE_CAT_IGNORE) {
|
||||
continue;
|
||||
}
|
||||
Profiler_GetHistory(i, &history[0], historyLength);
|
||||
|
||||
float x = 10;
|
||||
uint32_t col = nice_colors[i % ARRAY_SIZE(nice_colors)];
|
||||
if (area)
|
||||
col = opacity | (col & 0xFFFFFF);
|
||||
UI::Drawable color(col);
|
||||
UI::Drawable outline((opacity >> 1) | 0xFFFFFF);
|
||||
|
||||
if (area) {
|
||||
for (int n = 0; n < historyLength; n++) {
|
||||
float val = history[n];
|
||||
float valY1 = ui.GetBounds().y2() - 10 - (val + total[n]) * scale;
|
||||
float valY2 = ui.GetBounds().y2() - 10 - total[n] * scale;
|
||||
ui.FillRect(outline, Bounds(x, valY2, dx, 1.0f));
|
||||
ui.FillRect(color, Bounds(x, valY1, dx, valY2 - valY1));
|
||||
x += dx;
|
||||
total[n] += val;
|
||||
}
|
||||
} else {
|
||||
for (int n = 0; n < historyLength; n++) {
|
||||
float val = history[n];
|
||||
if (val > maxVal)
|
||||
maxVal = val;
|
||||
float valY = ui.GetBounds().y2() - 10 - history[n] * scale;
|
||||
ui.FillRect(color, Bounds(x, valY, dx, 5));
|
||||
x += dx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int n = 0; n < historyLength; n++) {
|
||||
if (total[n] > maxTotal)
|
||||
maxTotal = total[n];
|
||||
}
|
||||
|
||||
if (area) {
|
||||
maxVal = maxTotal;
|
||||
}
|
||||
|
||||
lastMaxVal = lastMaxVal * 0.95f + maxVal * 0.05f;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ShaderListScreen::CreateViews() {
|
||||
using namespace UI;
|
||||
|
||||
LinearLayout *layout = new LinearLayout(ORIENT_VERTICAL);
|
||||
root_ = layout;
|
||||
|
||||
tabs_ = new TabHolder(ORIENT_HORIZONTAL, 40, new LinearLayoutParams(1.0));
|
||||
layout->Add(tabs_);
|
||||
layout->Add(new Button("Back"))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
|
||||
|
||||
ScrollView *vs_scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
|
||||
ScrollView *fs_scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
|
||||
|
||||
LinearLayout *vshaderList = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
LinearLayout *fshaderList = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
|
||||
ListShaders(SHADER_TYPE_VERTEX, vshaderList);
|
||||
ListShaders(SHADER_TYPE_FRAGMENT, fshaderList);
|
||||
|
||||
vs_scroll->Add(vshaderList);
|
||||
fs_scroll->Add(fshaderList);
|
||||
|
||||
tabs_->AddTab("Vertex", vs_scroll);
|
||||
tabs_->AddTab("Fragment", fs_scroll);
|
||||
}
|
||||
|
||||
UI::EventReturn ShaderListScreen::OnShaderClick(UI::EventParams &e) {
|
||||
using namespace UI;
|
||||
std::string id = e.v->Tag();
|
||||
DebugShaderType type = SHADER_TYPE_VERTEX;
|
||||
if (tabs_->GetCurrentTab() == 1) {
|
||||
type = SHADER_TYPE_FRAGMENT;
|
||||
}
|
||||
screenManager()->push(new ShaderViewScreen(id, type));
|
||||
return EVENT_DONE;
|
||||
}
|
||||
|
||||
void ShaderViewScreen::CreateViews() {
|
||||
using namespace UI;
|
||||
|
||||
LinearLayout *layout = new LinearLayout(ORIENT_VERTICAL);
|
||||
root_ = layout;
|
||||
|
||||
layout->Add(new TextView(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SHORT_DESC)));
|
||||
|
||||
ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
|
||||
layout->Add(scroll);
|
||||
|
||||
LinearLayout *lineLayout = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
lineLayout->SetSpacing(0.0);
|
||||
scroll->Add(lineLayout);
|
||||
|
||||
std::vector<std::string> lines;
|
||||
SplitString(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SOURCE_CODE), '\n', lines);
|
||||
|
||||
for (auto line : lines) {
|
||||
lineLayout->Add(new TextView(line, FLAG_DYNAMIC_ASCII, true));
|
||||
}
|
||||
|
||||
layout->Add(new Button("Back"))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "ui/ui_screen.h"
|
||||
|
||||
#include "UI/MiscScreens.h"
|
||||
#include "GPU/Common/ShaderCommon.h"
|
||||
|
||||
class DevMenu : public PopupScreen {
|
||||
public:
|
||||
@ -38,6 +39,7 @@ protected:
|
||||
UI::EventReturn OnLogView(UI::EventParams &e);
|
||||
UI::EventReturn OnLogConfig(UI::EventParams &e);
|
||||
UI::EventReturn OnJitCompare(UI::EventParams &e);
|
||||
UI::EventReturn OnShaderView(UI::EventParams &e);
|
||||
UI::EventReturn OnFreezeFrame(UI::EventParams &e);
|
||||
UI::EventReturn OnDumpFrame(UI::EventParams &e);
|
||||
UI::EventReturn OnDeveloperTools(UI::EventParams &e);
|
||||
@ -141,5 +143,28 @@ private:
|
||||
UI::LinearLayout *rightDisasm_;
|
||||
};
|
||||
|
||||
class ShaderListScreen : public UIDialogScreenWithBackground {
|
||||
public:
|
||||
ShaderListScreen() {}
|
||||
void CreateViews() override;
|
||||
|
||||
private:
|
||||
void ListShaders(DebugShaderType shaderType, UI::LinearLayout *view);
|
||||
|
||||
UI::EventReturn OnShaderClick(UI::EventParams &e);
|
||||
|
||||
UI::TabHolder *tabs_;
|
||||
};
|
||||
|
||||
class ShaderViewScreen : public UIDialogScreenWithBackground {
|
||||
public:
|
||||
ShaderViewScreen(std::string id, DebugShaderType type)
|
||||
: id_(id), type_(type) {}
|
||||
|
||||
void CreateViews() override;
|
||||
private:
|
||||
std::string id_;
|
||||
DebugShaderType type_;
|
||||
};
|
||||
|
||||
void DrawProfile(UIContext &ui);
|
||||
|
@ -63,6 +63,7 @@
|
||||
#include "UI/ControlMappingScreen.h"
|
||||
#include "UI/GameSettingsScreen.h"
|
||||
#include "UI/InstallZipScreen.h"
|
||||
#include "UI/ProfilerDraw.h"
|
||||
|
||||
EmuScreen::EmuScreen(const std::string &filename)
|
||||
: bootPending_(true), gamePath_(filename), invalid_(true), quit_(false), pauseTrigger_(false), saveStatePreviewShownTime_(0.0), saveStatePreview_(nullptr) {
|
||||
|
192
UI/ProfilerDraw.cpp
Normal file
192
UI/ProfilerDraw.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
// 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 <inttypes.h>
|
||||
|
||||
#include "ui/ui_context.h"
|
||||
#include "profiler/profiler.h"
|
||||
|
||||
static const uint32_t nice_colors[] = {
|
||||
0xFF8040,
|
||||
0x80FF40,
|
||||
0x8040FF,
|
||||
0xFFFF40,
|
||||
|
||||
0x40FFFF,
|
||||
0xFF70FF,
|
||||
0xc0c0c0,
|
||||
0xb040c0,
|
||||
|
||||
0x184099,
|
||||
0xCC3333,
|
||||
0xFF99CC,
|
||||
0x3399CC,
|
||||
|
||||
0x990000,
|
||||
0x003366,
|
||||
0xF8F8F8,
|
||||
0x33FFFF,
|
||||
};
|
||||
|
||||
enum ProfileCatStatus {
|
||||
PROFILE_CAT_VISIBLE = 0,
|
||||
PROFILE_CAT_IGNORE = 1,
|
||||
PROFILE_CAT_NOLEGEND = 2,
|
||||
};
|
||||
|
||||
void DrawProfile(UIContext &ui) {
|
||||
#ifdef USE_PROFILER
|
||||
int numCategories = Profiler_GetNumCategories();
|
||||
int historyLength = Profiler_GetHistoryLength();
|
||||
|
||||
ui.SetFontStyle(ui.theme->uiFont);
|
||||
|
||||
static float lastMaxVal = 1.0f / 60.0f;
|
||||
float legendMinVal = lastMaxVal * (1.0f / 120.0f);
|
||||
|
||||
std::vector<float> history;
|
||||
std::vector<ProfileCatStatus> catStatus;
|
||||
history.resize(historyLength);
|
||||
catStatus.resize(numCategories);
|
||||
|
||||
float rowH = 30.0f;
|
||||
float legendHeight = 0.0f;
|
||||
float legendWidth = 80.0f;
|
||||
for (int i = 0; i < numCategories; i++) {
|
||||
const char *name = Profiler_GetCategoryName(i);
|
||||
if (!strcmp(name, "timing")) {
|
||||
catStatus[i] = PROFILE_CAT_IGNORE;
|
||||
continue;
|
||||
}
|
||||
|
||||
Profiler_GetHistory(i, &history[0], historyLength);
|
||||
catStatus[i] = PROFILE_CAT_NOLEGEND;
|
||||
for (int j = 0; j < historyLength; ++j) {
|
||||
if (history[j] > legendMinVal) {
|
||||
catStatus[i] = PROFILE_CAT_VISIBLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// So they don't move horizontally, we always measure.
|
||||
float w = 0.0f, h = 0.0f;
|
||||
ui.MeasureText(ui.GetFontStyle(), name, &w, &h);
|
||||
if (w > legendWidth) {
|
||||
legendWidth = w;
|
||||
}
|
||||
legendHeight += rowH;
|
||||
}
|
||||
legendWidth += 20.0f;
|
||||
|
||||
float legendStartY = legendHeight > ui.GetBounds().centerY() ? ui.GetBounds().y2() - legendHeight : ui.GetBounds().centerY();
|
||||
float legendStartX = ui.GetBounds().x2() - std::min(legendWidth, 200.0f);
|
||||
|
||||
const uint32_t opacity = 140 << 24;
|
||||
|
||||
int legendNum = 0;
|
||||
for (int i = 0; i < numCategories; i++) {
|
||||
const char *name = Profiler_GetCategoryName(i);
|
||||
uint32_t color = nice_colors[i % ARRAY_SIZE(nice_colors)];
|
||||
|
||||
if (catStatus[i] == PROFILE_CAT_VISIBLE) {
|
||||
float y = legendStartY + legendNum++ * rowH;
|
||||
ui.FillRect(UI::Drawable(opacity | color), Bounds(legendStartX, y, rowH - 2, rowH - 2));
|
||||
ui.DrawTextShadow(name, legendStartX + rowH + 2, y, 0xFFFFFFFF, ALIGN_VBASELINE);
|
||||
}
|
||||
}
|
||||
|
||||
float graphWidth = ui.GetBounds().x2() - legendWidth - 20.0f;
|
||||
float graphHeight = ui.GetBounds().h * 0.8f;
|
||||
|
||||
float dx = graphWidth / historyLength;
|
||||
|
||||
/*
|
||||
ui.Flush();
|
||||
|
||||
ui.BeginNoTex();
|
||||
*/
|
||||
|
||||
bool area = true;
|
||||
float minVal = 0.0f;
|
||||
float maxVal = lastMaxVal; // TODO - adjust to frame length
|
||||
if (maxVal < 0.001f)
|
||||
maxVal = 0.001f;
|
||||
if (maxVal > 1.0f / 15.0f)
|
||||
maxVal = 1.0f / 15.0f;
|
||||
|
||||
float scale = (graphHeight) / (maxVal - minVal);
|
||||
|
||||
float y_60th = ui.GetBounds().y2() - 10 - (1.0f / 60.0f) * scale;
|
||||
float y_1ms = ui.GetBounds().y2() - 10 - (1.0f / 1000.0f) * scale;
|
||||
|
||||
ui.FillRect(UI::Drawable(0x80FFFF00), Bounds(0, y_60th, graphWidth, 2));
|
||||
ui.FillRect(UI::Drawable(0x80FFFF00), Bounds(0, y_1ms, graphWidth, 2));
|
||||
ui.DrawTextShadow("1/60s", 5, y_60th, 0x80FFFF00);
|
||||
ui.DrawTextShadow("1ms", 5, y_1ms, 0x80FFFF00);
|
||||
|
||||
std::vector<float> total;
|
||||
total.resize(historyLength);
|
||||
|
||||
maxVal = 0.0f;
|
||||
float maxTotal = 0.0f;
|
||||
for (int i = 0; i < numCategories; i++) {
|
||||
if (catStatus[i] == PROFILE_CAT_IGNORE) {
|
||||
continue;
|
||||
}
|
||||
Profiler_GetHistory(i, &history[0], historyLength);
|
||||
|
||||
float x = 10;
|
||||
uint32_t col = nice_colors[i % ARRAY_SIZE(nice_colors)];
|
||||
if (area)
|
||||
col = opacity | (col & 0xFFFFFF);
|
||||
UI::Drawable color(col);
|
||||
UI::Drawable outline((opacity >> 1) | 0xFFFFFF);
|
||||
|
||||
if (area) {
|
||||
for (int n = 0; n < historyLength; n++) {
|
||||
float val = history[n];
|
||||
float valY1 = ui.GetBounds().y2() - 10 - (val + total[n]) * scale;
|
||||
float valY2 = ui.GetBounds().y2() - 10 - total[n] * scale;
|
||||
ui.FillRect(outline, Bounds(x, valY2, dx, 1.0f));
|
||||
ui.FillRect(color, Bounds(x, valY1, dx, valY2 - valY1));
|
||||
x += dx;
|
||||
total[n] += val;
|
||||
}
|
||||
} else {
|
||||
for (int n = 0; n < historyLength; n++) {
|
||||
float val = history[n];
|
||||
if (val > maxVal)
|
||||
maxVal = val;
|
||||
float valY = ui.GetBounds().y2() - 10 - history[n] * scale;
|
||||
ui.FillRect(color, Bounds(x, valY, dx, 5));
|
||||
x += dx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int n = 0; n < historyLength; n++) {
|
||||
if (total[n] > maxTotal)
|
||||
maxTotal = total[n];
|
||||
}
|
||||
|
||||
if (area) {
|
||||
maxVal = maxTotal;
|
||||
}
|
||||
|
||||
lastMaxVal = lastMaxVal * 0.95f + maxVal * 0.05f;
|
||||
#endif
|
||||
}
|
9
UI/ProfilerDraw.h
Normal file
9
UI/ProfilerDraw.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
class UIContext;
|
||||
|
||||
#ifdef USE_PROFILER
|
||||
|
||||
void DrawProfile(UIContext &ui);
|
||||
|
||||
#endif
|
@ -33,6 +33,7 @@
|
||||
<ClCompile Include="NativeApp.cpp" />
|
||||
<ClCompile Include="OnScreenDisplay.cpp" />
|
||||
<ClCompile Include="PauseScreen.cpp" />
|
||||
<ClCompile Include="ProfilerDraw.cpp" />
|
||||
<ClCompile Include="ReportScreen.cpp" />
|
||||
<ClCompile Include="SavedataScreen.cpp" />
|
||||
<ClCompile Include="Store.cpp" />
|
||||
@ -57,6 +58,7 @@
|
||||
<ClInclude Include="MiscScreens.h" />
|
||||
<ClInclude Include="OnScreenDisplay.h" />
|
||||
<ClInclude Include="PauseScreen.h" />
|
||||
<ClInclude Include="ProfilerDraw.h" />
|
||||
<ClInclude Include="ReportScreen.h" />
|
||||
<ClInclude Include="SavedataScreen.h" />
|
||||
<ClInclude Include="Store.h" />
|
||||
@ -217,4 +219,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -56,6 +56,9 @@
|
||||
<ClCompile Include="SavedataScreen.cpp">
|
||||
<Filter>Screens</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProfilerDraw.cpp">
|
||||
<Filter>Screens</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="GameInfoCache.h" />
|
||||
@ -112,6 +115,9 @@
|
||||
<ClInclude Include="SavedataScreen.h">
|
||||
<Filter>Screens</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ProfilerDraw.h">
|
||||
<Filter>Screens</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Screens">
|
||||
|
@ -562,7 +562,7 @@ namespace MainWindow
|
||||
static double lastMouseDown;
|
||||
double now = real_time_now();
|
||||
if ((now - lastMouseDown) < 0.001 * GetDoubleClickTime()) {
|
||||
if (!g_Config.bShowTouchControls && GetUIState() == UISTATE_INGAME) {
|
||||
if (!g_Config.bShowTouchControls && GetUIState() == UISTATE_INGAME && g_Config.bFullscreenOnDoubleclick) {
|
||||
SendToggleFullscreen(!g_Config.bFullScreen);
|
||||
}
|
||||
lastMouseDown = 0.0;
|
||||
|
@ -349,6 +349,7 @@ LOCAL_SRC_FILES := \
|
||||
$(SRC)/UI/TouchControlVisibilityScreen.cpp \
|
||||
$(SRC)/UI/CwCheatScreen.cpp \
|
||||
$(SRC)/UI/InstallZipScreen.cpp \
|
||||
$(SRC)/UI/ProfilerDraw.cpp \
|
||||
$(SRC)/UI/NativeApp.cpp
|
||||
|
||||
ifneq ($(SKIPAPP),1)
|
||||
|
Loading…
Reference in New Issue
Block a user