CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/GLES/TextureCacheGLES.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <algorithm>
19
#include <cstring>
20
21
#include "ext/xxhash.h"
22
#include "Common/Common.h"
23
#include "Common/Data/Convert/ColorConv.h"
24
#include "Common/Data/Text/I18n.h"
25
#include "Common/Math/math_util.h"
26
#include "Common/Profiler/Profiler.h"
27
#include "Common/System/OSD.h"
28
#include "Common/GPU/OpenGL/GLRenderManager.h"
29
#include "Common/TimeUtil.h"
30
31
#include "Core/Config.h"
32
#include "Core/MemMap.h"
33
#include "GPU/ge_constants.h"
34
#include "GPU/GPUState.h"
35
#include "GPU/GLES/TextureCacheGLES.h"
36
#include "GPU/GLES/FramebufferManagerGLES.h"
37
#include "GPU/Common/FragmentShaderGenerator.h"
38
#include "GPU/Common/TextureShaderCommon.h"
39
#include "GPU/GLES/ShaderManagerGLES.h"
40
#include "GPU/GLES/DrawEngineGLES.h"
41
#include "GPU/Common/TextureDecoder.h"
42
43
#ifdef _M_SSE
44
#include <emmintrin.h>
45
#endif
46
47
TextureCacheGLES::TextureCacheGLES(Draw::DrawContext *draw, Draw2D *draw2D)
48
: TextureCacheCommon(draw, draw2D) {
49
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
50
51
nextTexture_ = nullptr;
52
}
53
54
TextureCacheGLES::~TextureCacheGLES() {
55
Clear(true);
56
}
57
58
void TextureCacheGLES::SetFramebufferManager(FramebufferManagerGLES *fbManager) {
59
framebufferManagerGL_ = fbManager;
60
framebufferManager_ = fbManager;
61
}
62
63
void TextureCacheGLES::ReleaseTexture(TexCacheEntry *entry, bool delete_them) {
64
if (delete_them) {
65
if (entry->textureName) {
66
render_->DeleteTexture(entry->textureName);
67
}
68
}
69
entry->textureName = nullptr;
70
}
71
72
void TextureCacheGLES::Clear(bool delete_them) {
73
TextureCacheCommon::Clear(delete_them);
74
}
75
76
Draw::DataFormat getClutDestFormat(GEPaletteFormat format) {
77
switch (format) {
78
case GE_CMODE_16BIT_ABGR4444:
79
return Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
80
case GE_CMODE_16BIT_ABGR5551:
81
return Draw::DataFormat::R5G5B5A1_UNORM_PACK16;
82
case GE_CMODE_16BIT_BGR5650:
83
return Draw::DataFormat::R5G6B5_UNORM_PACK16;
84
case GE_CMODE_32BIT_ABGR8888:
85
return Draw::DataFormat::R8G8B8A8_UNORM;
86
}
87
return Draw::DataFormat::UNDEFINED;
88
}
89
90
static const GLuint MinFiltGL[8] = {
91
GL_NEAREST,
92
GL_LINEAR,
93
GL_NEAREST,
94
GL_LINEAR,
95
GL_NEAREST_MIPMAP_NEAREST,
96
GL_LINEAR_MIPMAP_NEAREST,
97
GL_NEAREST_MIPMAP_LINEAR,
98
GL_LINEAR_MIPMAP_LINEAR,
99
};
100
101
static const GLuint MagFiltGL[2] = {
102
GL_NEAREST,
103
GL_LINEAR
104
};
105
106
void TextureCacheGLES::ApplySamplingParams(const SamplerCacheKey &key) {
107
if (gstate_c.Use(GPU_USE_TEXTURE_LOD_CONTROL)) {
108
float minLod = (float)key.minLevel / 256.0f;
109
float maxLod = (float)key.maxLevel / 256.0f;
110
float lodBias = (float)key.lodBias / 256.0f;
111
render_->SetTextureLod(0, minLod, maxLod, lodBias);
112
}
113
114
float aniso = 0.0f;
115
int minKey = ((int)key.mipEnable << 2) | ((int)key.mipFilt << 1) | ((int)key.minFilt);
116
render_->SetTextureSampler(0,
117
key.sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, key.tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT,
118
key.magFilt ? GL_LINEAR : GL_NEAREST, MinFiltGL[minKey], aniso);
119
}
120
121
static void ConvertColors(void *dstBuf, const void *srcBuf, Draw::DataFormat dstFmt, int numPixels) {
122
const u32 *src = (const u32 *)srcBuf;
123
u32 *dst = (u32 *)dstBuf;
124
switch (dstFmt) {
125
case Draw::DataFormat::R4G4B4A4_UNORM_PACK16:
126
ConvertRGBA4444ToABGR4444((u16 *)dst, (const u16 *)src, numPixels);
127
break;
128
// Final Fantasy 2 uses this heavily in animated textures.
129
case Draw::DataFormat::R5G5B5A1_UNORM_PACK16:
130
ConvertRGBA5551ToABGR1555((u16 *)dst, (const u16 *)src, numPixels);
131
break;
132
case Draw::DataFormat::R5G6B5_UNORM_PACK16:
133
ConvertRGB565ToBGR565((u16 *)dst, (const u16 *)src, numPixels);
134
break;
135
default:
136
// No need to convert RGBA8888, right order already
137
if (dst != src)
138
memcpy(dst, src, numPixels * sizeof(u32));
139
break;
140
}
141
}
142
143
void TextureCacheGLES::StartFrame() {
144
TextureCacheCommon::StartFrame();
145
146
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
147
if (!lowMemoryMode_ && renderManager->SawOutOfMemory()) {
148
lowMemoryMode_ = true;
149
decimationCounter_ = 0;
150
151
auto err = GetI18NCategory(I18NCat::ERRORS);
152
if (standardScaleFactor_ > 1) {
153
g_OSD.Show(OSDType::MESSAGE_WARNING, err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f);
154
} else {
155
g_OSD.Show(OSDType::MESSAGE_WARNING, err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f);
156
}
157
}
158
}
159
160
void TextureCacheGLES::UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) {
161
const u32 clutBaseBytes = clutFormat == GE_CMODE_32BIT_ABGR8888 ? (clutBase * sizeof(u32)) : (clutBase * sizeof(u16));
162
// Technically, these extra bytes weren't loaded, but hopefully it was loaded earlier.
163
// If not, we're going to hash random data, which hopefully doesn't cause a performance issue.
164
//
165
// TODO: Actually, this seems like a hack. The game can upload part of a CLUT and reference other data.
166
// clutTotalBytes_ is the last amount uploaded. We should hash clutMaxBytes_, but this will often hash
167
// unrelated old entries for small palettes.
168
// Adding clutBaseBytes may just be mitigating this for some usage patterns.
169
const u32 clutExtendedBytes = std::min(clutTotalBytes_ + clutBaseBytes, clutMaxBytes_);
170
171
if (replacer_.Enabled())
172
clutHash_ = XXH32((const char *)clutBufRaw_, clutExtendedBytes, 0xC0108888);
173
else
174
clutHash_ = XXH3_64bits((const char *)clutBufRaw_, clutExtendedBytes) & 0xFFFFFFFF;
175
176
// Avoid a copy when we don't need to convert colors.
177
if (clutFormat != GE_CMODE_32BIT_ABGR8888) {
178
const int numColors = clutFormat == GE_CMODE_32BIT_ABGR8888 ? (clutMaxBytes_ / sizeof(u32)) : (clutMaxBytes_ / sizeof(u16));
179
ConvertColors(clutBufConverted_, clutBufRaw_, getClutDestFormat(clutFormat), numColors);
180
clutBuf_ = clutBufConverted_;
181
} else {
182
clutBuf_ = clutBufRaw_;
183
}
184
185
// Special optimization: fonts typically draw clut4 with just alpha values in a single color.
186
clutAlphaLinear_ = false;
187
clutAlphaLinearColor_ = 0;
188
if (clutFormat == GE_CMODE_16BIT_ABGR4444 && clutIndexIsSimple) {
189
const u16_le *clut = GetCurrentClut<u16_le>();
190
clutAlphaLinear_ = true;
191
clutAlphaLinearColor_ = clut[15] & 0xFFF0;
192
for (int i = 0; i < 16; ++i) {
193
u16 step = clutAlphaLinearColor_ | i;
194
if (clut[i] != step) {
195
clutAlphaLinear_ = false;
196
break;
197
}
198
}
199
}
200
201
clutLastFormat_ = gstate.clutformat;
202
}
203
204
void TextureCacheGLES::BindTexture(TexCacheEntry *entry) {
205
if (!entry) {
206
render_->BindTexture(0, nullptr);
207
lastBoundTexture = nullptr;
208
return;
209
}
210
if (entry->textureName != lastBoundTexture) {
211
render_->BindTexture(0, entry->textureName);
212
lastBoundTexture = entry->textureName;
213
}
214
int maxLevel = (entry->status & TexCacheEntry::STATUS_NO_MIPS) ? 0 : entry->maxLevel;
215
SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry);
216
ApplySamplingParams(samplerKey);
217
gstate_c.SetUseShaderDepal(ShaderDepalMode::OFF);
218
}
219
220
void TextureCacheGLES::Unbind() {
221
render_->BindTexture(TEX_SLOT_PSP_TEXTURE, nullptr);
222
ForgetLastTexture();
223
}
224
225
void TextureCacheGLES::BindAsClutTexture(Draw::Texture *tex, bool smooth) {
226
GLRTexture *glrTex = (GLRTexture *)draw_->GetNativeObject(Draw::NativeObject::TEXTURE_VIEW, tex);
227
render_->BindTexture(TEX_SLOT_CLUT, glrTex);
228
render_->SetTextureSampler(TEX_SLOT_CLUT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, smooth ? GL_LINEAR : GL_NEAREST, smooth ? GL_LINEAR : GL_NEAREST, 0.0f);
229
}
230
231
void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
232
BuildTexturePlan plan;
233
if (!PrepareBuildTexture(plan, entry)) {
234
// We're screwed?
235
return;
236
}
237
238
_assert_(!entry->textureName);
239
240
// GLES2 doesn't have support for a "Max lod" which is critical as PSP games often
241
// don't specify mips all the way down. As a result, we either need to manually generate
242
// the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not
243
// be as good quality as the game's own (might even be better in some cases though).
244
245
int tw = plan.createW;
246
int th = plan.createH;
247
248
Draw::DataFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());
249
if (plan.doReplace) {
250
plan.replaced->GetSize(plan.baseLevelSrc, &tw, &th);
251
dstFmt = plan.replaced->Format();
252
} else if (plan.scaleFactor > 1 || plan.saveTexture) {
253
dstFmt = Draw::DataFormat::R8G8B8A8_UNORM;
254
} else if (plan.decodeToClut8) {
255
dstFmt = Draw::DataFormat::R8_UNORM;
256
}
257
258
if (plan.depth == 1) {
259
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D, tw, th, 1, plan.levelsToCreate);
260
} else {
261
entry->textureName = render_->CreateTexture(GL_TEXTURE_3D, tw, th, plan.depth, 1);
262
}
263
264
// Apply some additional compatibility checks.
265
if (plan.levelsToLoad > 1) {
266
// Avoid PowerVR driver bug
267
if (plan.w > 1 && plan.h > 1 && !(plan.h > plan.w && draw_->GetBugs().Has(Draw::Bugs::PVR_GENMIPMAP_HEIGHT_GREATER))) { // Really! only seems to fail if height > width
268
// It's ok to generate mipmaps beyond the loaded levels.
269
} else {
270
plan.levelsToCreate = plan.levelsToLoad;
271
}
272
}
273
274
if (!gstate_c.Use(GPU_USE_TEXTURE_LOD_CONTROL)) {
275
// If the mip chain is not full..
276
if (plan.levelsToCreate != plan.maxPossibleLevels) {
277
// We need to avoid creating mips at all, or generate them all - can't be incomplete
278
// on this hardware (strict OpenGL rules).
279
plan.levelsToCreate = 1;
280
plan.levelsToLoad = 1;
281
entry->status |= TexCacheEntry::STATUS_NO_MIPS;
282
}
283
}
284
285
if (plan.depth == 1) {
286
for (int i = 0; i < plan.levelsToLoad; i++) {
287
int srcLevel = i == 0 ? plan.baseLevelSrc : i;
288
289
int mipWidth;
290
int mipHeight;
291
plan.GetMipSize(i, &mipWidth, &mipHeight);
292
293
u8 *data = nullptr;
294
int stride = 0;
295
int dataSize;
296
297
bool bc = false;
298
299
if (plan.doReplace) {
300
int blockSize = 0;
301
if (Draw::DataFormatIsBlockCompressed(plan.replaced->Format(), &blockSize)) {
302
stride = mipWidth * 4;
303
dataSize = plan.replaced->GetLevelDataSizeAfterCopy(i);
304
bc = true;
305
} else {
306
int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format());
307
stride = mipWidth * bpp;
308
dataSize = stride * mipHeight;
309
}
310
} else {
311
int bpp = 0;
312
if (plan.scaleFactor > 1) {
313
bpp = 4;
314
} else {
315
bpp = (int)Draw::DataFormatSizeInBytes(dstFmt);
316
}
317
stride = mipWidth * bpp;
318
dataSize = stride * mipHeight;
319
}
320
321
data = (u8 *)AllocateAlignedMemory(dataSize, 16);
322
323
if (!data) {
324
ERROR_LOG(Log::G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", mipWidth, mipHeight);
325
return;
326
}
327
328
LoadTextureLevel(*entry, data, dataSize, stride, plan, srcLevel, dstFmt, TexDecodeFlags::REVERSE_COLORS);
329
330
// NOTE: TextureImage takes ownership of data, so we don't free it afterwards.
331
render_->TextureImage(entry->textureName, i, mipWidth, mipHeight, 1, dstFmt, data, GLRAllocType::ALIGNED);
332
}
333
334
bool genMips = plan.levelsToCreate > plan.levelsToLoad;
335
336
render_->FinalizeTexture(entry->textureName, plan.levelsToLoad, genMips);
337
} else {
338
int bpp = (int)Draw::DataFormatSizeInBytes(dstFmt);
339
int stride = bpp * (plan.w * plan.scaleFactor);
340
int levelStride = stride * (plan.h * plan.scaleFactor);
341
342
size_t dataSize = levelStride * plan.depth;
343
u8 *data = (u8 *)AllocateAlignedMemory(dataSize, 16);
344
memset(data, 0, levelStride * plan.depth);
345
u8 *p = data;
346
347
for (int i = 0; i < plan.depth; i++) {
348
LoadTextureLevel(*entry, p, dataSize, stride, plan, i, dstFmt, TexDecodeFlags::REVERSE_COLORS);
349
p += levelStride;
350
}
351
352
render_->TextureImage(entry->textureName, 0, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, dstFmt, data, GLRAllocType::ALIGNED);
353
354
// Signal that we support depth textures so use it as one.
355
entry->status |= TexCacheEntry::STATUS_3D;
356
357
render_->FinalizeTexture(entry->textureName, 1, false);
358
}
359
360
if (plan.doReplace) {
361
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
362
}
363
}
364
365
Draw::DataFormat TextureCacheGLES::GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) {
366
switch (format) {
367
case GE_TFMT_CLUT4:
368
case GE_TFMT_CLUT8:
369
case GE_TFMT_CLUT16:
370
case GE_TFMT_CLUT32:
371
return getClutDestFormat(clutFormat);
372
case GE_TFMT_4444:
373
return Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
374
case GE_TFMT_5551:
375
return Draw::DataFormat::R5G5B5A1_UNORM_PACK16;
376
case GE_TFMT_5650:
377
return Draw::DataFormat::R5G6B5_UNORM_PACK16;
378
case GE_TFMT_8888:
379
case GE_TFMT_DXT1:
380
case GE_TFMT_DXT3:
381
case GE_TFMT_DXT5:
382
default:
383
return Draw::DataFormat::R8G8B8A8_UNORM;
384
}
385
}
386
387
bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level, bool *isFramebuffer) {
388
ForgetLastTexture();
389
SetTexture();
390
if (!nextTexture_) {
391
return GetCurrentFramebufferTextureDebug(buffer, isFramebuffer);
392
}
393
394
// Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer.
395
TexCacheEntry *entry = nextTexture_;
396
// We might need a render pass to set the sampling params, unfortunately. Otherwise BuildTexture may crash.
397
framebufferManagerGL_->RebindFramebuffer("RebindFramebuffer - GetCurrentTextureDebug");
398
ApplyTexture();
399
400
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
401
402
// Not a framebuffer, so let's assume these are right.
403
// TODO: But they may definitely not be, if the texture was scaled.
404
int w = gstate.getTextureWidth(level);
405
int h = gstate.getTextureHeight(level);
406
407
bool result = entry->textureName != nullptr;
408
if (result) {
409
buffer.Allocate(w, h, GE_FORMAT_8888, false);
410
renderManager->CopyImageToMemorySync(entry->textureName, level, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, (uint8_t *)buffer.GetData(), w, "GetCurrentTextureDebug");
411
} else {
412
ERROR_LOG(Log::G3D, "Failed to get debug texture: texture is null");
413
}
414
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
415
framebufferManager_->RebindFramebuffer("RebindFramebuffer - GetCurrentTextureDebug");
416
417
*isFramebuffer = false;
418
return result;
419
}
420
421
void TextureCacheGLES::DeviceLost() {
422
textureShaderCache_->DeviceLost();
423
Clear(false);
424
draw_ = nullptr;
425
render_ = nullptr;
426
}
427
428
void TextureCacheGLES::DeviceRestore(Draw::DrawContext *draw) {
429
draw_ = draw;
430
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
431
textureShaderCache_->DeviceRestore(draw);
432
}
433
434
void *TextureCacheGLES::GetNativeTextureView(const TexCacheEntry *entry) {
435
GLRTexture *tex = entry->textureName;
436
return (void *)tex;
437
}
438
439