Merge pull request #8454 from unknownbrackets/gpu-depth-round

Add pixel depth rounding to d3d9, gles2 and support forcing via config
This commit is contained in:
Henrik Rydgård committed 2016-01-21 09:41:26 +01:00
commit cd35684a46
19 files changed
+218 -27

No files matched your search

+1
View File
@@ -442,6 +442,7 @@ static ConfigSetting graphicsSettings[] = {
ReportedConfigSetting("BufferFiltering", &g_Config.iBufFilter, 1, true, true),
ReportedConfigSetting("InternalResolution", &g_Config.iInternalResolution, &DefaultInternalResolution, true, true),
ReportedConfigSetting("AndroidHwScale", &g_Config.iAndroidHwScale, &DefaultAndroidHwScale),
ReportedConfigSetting("HighQualityDepth", &g_Config.bHighQualityDepth, true, true, true),
ReportedConfigSetting("FrameSkip", &g_Config.iFrameSkip, 0, true, true),
ReportedConfigSetting("AutoFrameSkip", &g_Config.bAutoFrameSkip, false, true, true),
ReportedConfigSetting("FrameRate", &g_Config.iFpsLimit, 0, true, true),
+1
View File
@@ -173,6 +173,7 @@ public:
bool bFullScreen;
int iInternalResolution; // 0 = Auto (native), 1 = 1x (480x272), 2 = 2x, 3 = 3x, 4 = 4x and so on.
int iAnisotropyLevel; // 0 - 5, powers of 2: 0 = 1x = no aniso
int bHighQualityDepth;
bool bTrueColor;
bool bMipMap;
int iTexScalingLevel; // 1 = off, 2 = 2x, ..., 5 = 5x
+2
View File
@@ -310,6 +310,8 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
vfb->fb_stride = params.fb_stride;
vfb->format = params.fmt;
}
// Keep track, but this isn't really used.
vfb->z_stride = params.z_stride;
// Heuristic: In throughmode, a higher height could be used. Let's avoid shrinking the buffer.
if (params.isModeThrough && (int)vfb->width < params.fb_stride) {
vfb->width = std::max((int)vfb->width, drawing_width);
+2
View File
@@ -57,6 +57,8 @@ u32 GPUDebugBuffer::PixelSize(GPUDebugBufferFormat fmt) const {
case GPU_DBG_FORMAT_FLOAT:
case GPU_DBG_FORMAT_24BIT_8X:
case GPU_DBG_FORMAT_24X_8BIT:
case GPU_DBG_FORMAT_FLOAT_DIV_256:
case GPU_DBG_FORMAT_24BIT_8X_DIV_256:
return 4;
case GPU_DBG_FORMAT_888_RGB:
+3
View File
@@ -59,6 +59,9 @@ enum GPUDebugBufferFormat {
GPU_DBG_FORMAT_24BIT_8X = 0x13,
GPU_DBG_FORMAT_24X_8BIT = 0x14,
GPU_DBG_FORMAT_FLOAT_DIV_256 = 0x18,
GPU_DBG_FORMAT_24BIT_8X_DIV_256 = 0x1B,
// This is used for screenshots, mainly.
GPU_DBG_FORMAT_888_RGB = 0x20,
};
+12 -6
View File
@@ -498,10 +498,19 @@ LogicOpReplaceType ReplaceLogicOpType() {
return LOGICOPTYPE_NORMAL;
}
static const float depthSliceFactor = 4.0f;
static const float DEPTH_SLICE_FACTOR_HIGH = 4.0f;
static const float DEPTH_SLICE_FACTOR_16BIT = 256.0f;
float DepthSliceFactor() {
if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) {
return DEPTH_SLICE_FACTOR_16BIT;
}
return DEPTH_SLICE_FACTOR_HIGH;
}
// This is used for float values which might not be integers, but are in the integer scale of 65535.
static float ToScaledDepthFromInteger(float z) {
const float depthSliceFactor = DepthSliceFactor();
const float offset = 0.5f * (depthSliceFactor - 1.0f) * (1.0f / depthSliceFactor);
return z * (1.0f / depthSliceFactor) * (1.0f / 65535.0f) + offset;
}
@@ -511,14 +520,11 @@ float ToScaledDepth(u16 z) {
}
float FromScaledDepth(float z) {
const float depthSliceFactor = DepthSliceFactor();
const float offset = 0.5f * (depthSliceFactor - 1.0f) * (1.0f / depthSliceFactor);
return (z - offset) * depthSliceFactor * 65535.0f;
}
float DepthSliceFactor() {
return depthSliceFactor;
}
void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out) {
bool throughmode = gstate.isModeThrough();
out.dirtyProj = false;
@@ -662,7 +668,7 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
// Here, we should "clamp." But clamping per fragment would be slow.
// So, instead, we just increase the available range and hope.
// If depthSliceFactor is 4, it means (75% / 2) of the depth lies in each direction.
float fullDepthRange = 65535.0f * (depthSliceFactor - 1.0f) * (1.0f / 2.0f);
float fullDepthRange = 65535.0f * (DepthSliceFactor() - 1.0f) * (1.0f / 2.0f);
if (minz == 0) {
minz -= fullDepthRange;
}
+52
View File
@@ -1055,6 +1055,55 @@ namespace DX9 {
}
}
void FramebufferManagerDX9::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
if (!vfb->fbo) {
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0");
return;
}
// We always read the depth buffer in 24_8 format.
const u32 z_address = (0x04000000) | vfb->z_address;
DEBUG_LOG(SCEGE, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address);
LPDIRECT3DTEXTURE9 tex = fbo_get_depth_texture(vfb->fbo_dx9);
if (tex) {
D3DSURFACE_DESC desc;
D3DLOCKED_RECT locked;
tex->GetLevelDesc(0, &desc);
RECT rect = {0, 0, (LONG)desc.Width, (LONG)desc.Height};
HRESULT hr = tex->LockRect(0, &locked, &rect, D3DLOCK_READONLY);
if (SUCCEEDED(hr)) {
const int dstByteOffset = y * vfb->fb_stride * sizeof(s16);
const u32 *packed = (const u32 *)locked.pBits;
u16 *depth = (u16 *)Memory::GetPointer(z_address);
// TODO: Optimize.
for (int yp = 0; yp < h; ++yp) {
for (int xp = 0; xp < w; ++xp) {
const int offset = (yp + y) & vfb->z_stride + x + xp;
float scaled = FromScaledDepth((packed[offset] & 0x00FFFFFF) * (1.0f / 16777215.0f));
if (scaled <= 0.0f) {
depth[offset] = 0;
} else if (scaled >= 65535.0f) {
depth[offset] = 65535;
} else {
depth[offset] = (int)scaled;
}
}
}
tex->UnlockRect(0);
} else {
ERROR_LOG_REPORT(G3D, "Unable to lock rect from depth %08x: %d,%d %dx%d of %dx%d", vfb->fb_address, rect.left, rect.top, rect.right, rect.bottom, vfb->renderWidth, vfb->renderHeight);
}
} else {
ERROR_LOG_REPORT(G3D, "Unable to download render target depth from %08x", vfb->fb_address);
}
}
void FramebufferManagerDX9::EndFrame() {
if (resized_) {
DestroyAllFBOs();
@@ -1320,6 +1369,9 @@ namespace DX9 {
if (SUCCEEDED(hr)) {
GPUDebugBufferFormat fmt = GPU_DBG_FORMAT_24BIT_8X;
if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) {
fmt = GPU_DBG_FORMAT_24BIT_8X_DIV_256;
}
int pixelSize = 4;
buffer.Allocate(locked.Pitch / pixelSize, desc.Height, fmt, false);
+1
View File
@@ -115,6 +115,7 @@ private:
void SetNumExtraFBOs(int num);
void PackFramebufferDirectx9_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h);
static bool GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer);
// Used by DrawPixels
+6 -1
View File
@@ -478,7 +478,12 @@ void DIRECTX9_GPU::CheckGPUFeatures() {
features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL;
features |= GPU_PREFER_CPU_DOWNLOAD;
if (PSP_CoreParameter().compat.flags().VertexDepthRounding) {
if (!g_Config.bHighQualityDepth) {
features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT;
} else if (PSP_CoreParameter().compat.flags().PixelDepthRounding) {
// Assume we always have a 24-bit depth buffer.
features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT;
} else if (PSP_CoreParameter().compat.flags().VertexDepthRounding) {
features |= GPU_ROUND_DEPTH_TO_16BIT;
}
+2 -1
View File
@@ -488,7 +488,8 @@ void ShaderManagerDX9::VSUpdateUniforms(int dirtyUniforms) {
float halfActualZRange = vpZScale / gstate_c.vpDepthScale;
float minz = -((gstate_c.vpZOffset * halfActualZRange) - vpZCenter) - halfActualZRange;
float viewZScale = halfActualZRange * 2.0f;
float viewZCenter = minz;
// Account for the half pixel offset.
float viewZCenter = minz + (DepthSliceFactor() / 256.0f) * 0.5f;
float viewZInvScale;
if (viewZScale != 0.0) {
+13
View File
@@ -143,6 +143,18 @@ int fbo_check_framebuffer_status(FBO *fbo) {
return (int)fbStatus;
}
int fbo_standard_z_depth() {
// This matches the fbo_create() logic.
if (gl_extensions.IsGLES) {
if (gl_extensions.OES_packed_depth_stencil) {
return 24;
}
return gl_extensions.OES_depth24 ? 24 : 16;
} else {
return 24;
}
}
FBO *fbo_create(int width, int height, int num_color_textures, bool z_stencil, FBOColorDepth colorDepth) {
CheckGLExtensions();
@@ -216,6 +228,7 @@ FBO *fbo_create(int width, int height, int num_color_textures, bool z_stencil, F
// 16/24-bit Z, separate 8-bit stencil
glGenRenderbuffers(1, &fbo->z_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_buffer);
// Don't forget to make sure fbo_standard_z_depth() matches.
glRenderbufferStorage(GL_RENDERBUFFER, gl_extensions.OES_depth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, width, height);
// 8-bit stencil buffer
+2
View File
@@ -43,6 +43,8 @@ enum FBOColorDepth {
// On some hardware, you might get a 24-bit depth buffer even though you only wanted a 16-bit one.
FBO *fbo_create(int width, int height, int num_color_textures, bool z_stencil, FBOColorDepth colorDepth = FBO_8888);
int fbo_standard_z_depth();
// Create an opaque FBO from a native GL FBO, optionally reusing an existing FBO structure.
// Useful for overriding the backbuffer FBO that is generated outside of this wrapper.
FBO *fbo_create_from_native_fbo(GLuint native_fbo, FBO *fbo = NULL);
+60 -14
View File
@@ -1677,30 +1677,30 @@ void FramebufferManager::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, in
}
// Pixel size always 4 here because we always request RGBA8888
size_t bufSize = vfb->fb_stride * std::max(vfb->height, (u16)h) * 4;
u32 bufSize = vfb->fb_stride * (h - y) * 4;
u32 fb_address = (0x04000000) | vfb->fb_address;
GLubyte *packed = 0;
bool convert = vfb->format != GE_FORMAT_8888 || UseBGRA8888();
const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2;
const int packWidth = x + w < vfb->width ? x + w : vfb->width;
const int packWidth = std::min(vfb->fb_stride, std::min(x + w, (int)vfb->width));
if (!convert) {
packed = (GLubyte *)Memory::GetPointer(fb_address);
} else { // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address
u32 neededSize = (u32)bufSize * sizeof(GLubyte);
if (!convBuf_ || convBufSize_ < neededSize) {
int byteOffset = y * vfb->fb_stride * 4;
packed = (GLubyte *)Memory::GetPointer(fb_address + byteOffset);
} else {
// End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address
if (!convBuf_ || convBufSize_ < bufSize) {
delete [] convBuf_;
convBuf_ = new u8[neededSize];
convBufSize_ = neededSize;
convBuf_ = new u8[bufSize];
convBufSize_ = bufSize;
}
packed = convBuf_;
}
if (packed) {
DEBUG_LOG(SCEGE, "Reading framebuffer to mem, bufSize = %u, packed = %p, fb_address = %08x",
(u32)bufSize, packed, fb_address);
DEBUG_LOG(SCEGE, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
GLenum glfmt = GL_RGBA;
@@ -1708,12 +1708,11 @@ void FramebufferManager::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, in
glfmt = GL_BGRA_EXT;
}
int byteOffset = y * vfb->fb_stride * 4;
SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->fb_stride, h, glfmt, GL_UNSIGNED_BYTE, packed + byteOffset);
SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->fb_stride, h, glfmt, GL_UNSIGNED_BYTE, packed);
if (convert) {
int dstByteOffset = y * vfb->fb_stride * dstBpp;
ConvertFromRGBA8888(Memory::GetPointer(fb_address + dstByteOffset), packed + byteOffset, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format);
ConvertFromRGBA8888(Memory::GetPointer(fb_address + dstByteOffset), packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format);
}
}
@@ -1732,6 +1731,49 @@ void FramebufferManager::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, in
fbo_unbind_read();
}
void FramebufferManager::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
if (vfb->fbo) {
fbo_bind_for_read(vfb->fbo);
} else {
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0");
return;
}
// Pixel size always 4 here because we always request float
const u32 bufSize = vfb->z_stride * (h - y) * 4;
const u32 z_address = (0x04000000) | vfb->z_address;
const int packWidth = std::min(vfb->z_stride, std::min(x + w, (int)vfb->width));
if (!convBuf_ || convBufSize_ < bufSize) {
delete [] convBuf_;
convBuf_ = new u8[bufSize];
convBufSize_ = bufSize;
}
DEBUG_LOG(SCEGE, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->z_stride, h, GL_DEPTH_COMPONENT, GL_FLOAT, convBuf_);
int dstByteOffset = y * vfb->fb_stride * sizeof(u16);
u16 *depth = (u16 *)Memory::GetPointer(z_address + dstByteOffset);
GLfloat *packed = (GLfloat *)convBuf_;
int totalPixels = h == 1 ? packWidth : vfb->z_stride * h;
for (int i = 0; i < totalPixels; ++i) {
float scaled = FromScaledDepth(packed[i]);
if (scaled <= 0.0f) {
depth[i] = 0;
} else if (scaled >= 65535.0f) {
depth[i] = 65535;
} else {
depth[i] = (int)scaled;
}
}
fbo_unbind_read();
}
#ifdef _WIN32
void ShowScreenResolution();
#endif
@@ -1972,7 +2014,11 @@ bool FramebufferManager::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_add
return true;
}
buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, false);
if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) {
buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT_DIV_256, false);
} else {
buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, false);
}
if (vfb->fbo)
fbo_bind_for_read(vfb->fbo);
if (gl_extensions.GLES3 || !gl_extensions.IsGLES)
+1
View File
@@ -145,6 +145,7 @@ private:
void PackFramebufferAsync_(VirtualFramebuffer *vfb); // Not used under ES currently
void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h);
// Used by DrawPixels
unsigned int drawPixelsTex_;
+17 -5
View File
@@ -565,11 +565,23 @@ void GLES_GPU::CheckGPUFeatures() {
if (gl_extensions.GLES3 || !gl_extensions.IsGLES)
features |= GPU_SUPPORTS_TEXTURE_LOD_CONTROL;
// In the future, also disable this when we get a proper 16-bit depth buffer.
if ((!gl_extensions.IsGLES || gl_extensions.GLES3) && PSP_CoreParameter().compat.flags().PixelDepthRounding) {
features |= GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT;
} else {
if (PSP_CoreParameter().compat.flags().VertexDepthRounding) {
// If we already have a 16-bit depth buffer, we don't need to round.
if (fbo_standard_z_depth() > 16) {
if (!g_Config.bHighQualityDepth) {
features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT;
} else if (PSP_CoreParameter().compat.flags().PixelDepthRounding) {
if (!gl_extensions.IsGLES || gl_extensions.GLES3) {
// Use fragment rounding on desktop and GLES3, most accurate.
features |= GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT;
} else if (fbo_standard_z_depth() == 24) {
// Here we can simulate a 16 bit depth buffer by scaling.
// Note that the depth buffer is fixed point, not floating, so dividing by 256 is pretty good.
features |= GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT;
} else {
// At least do vertex rounding if nothing else.
features |= GPU_ROUND_DEPTH_TO_16BIT;
}
} else if (PSP_CoreParameter().compat.flags().VertexDepthRounding) {
features |= GPU_ROUND_DEPTH_TO_16BIT;
}
}
+1
View File
@@ -458,6 +458,7 @@ enum {
GPU_SUPPORTS_VAO = FLAG_BIT(18),
GPU_SUPPORTS_ANY_COPY_IMAGE = FLAG_BIT(19),
GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH = FLAG_BIT(20),
GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT = FLAG_BIT(21),
GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT = FLAG_BIT(22),
GPU_ROUND_DEPTH_TO_16BIT = FLAG_BIT(23), // Can be disabled either per game or if we use a real 16-bit depth buffer
GPU_SUPPORTS_TEXTURE_LOD_CONTROL = FLAG_BIT(24),
+18
View File
@@ -402,6 +402,15 @@ void CGEDebugger::DescribePixel(u32 pix, GPUDebugBufferFormat fmt, int x, int y,
_snwprintf(desc, 256, L"%d,%d: %d / %f / %f", x, y, pix & 0x00FFFFFF, (pix & 0x00FFFFFF) * (1.0f / 16777215.0f), FromScaledDepth((pix & 0x00FFFFFF) * (1.0f / 16777215.0f)));
break;
case GPU_DBG_FORMAT_24BIT_8X_DIV_256:
{
// These are only ever going to be depth values, so let's also show scaled to 16 bit.
int z24 = pix & 0x00FFFFFF;
int z16 = z24 - 0x800000 + 0x8000;
_snwprintf(desc, 256, L"%d,%d: %d / %f", x, y, z16, z16 * (1.0f / 65535.0f));
}
break;
case GPU_DBG_FORMAT_24X_8BIT:
_snwprintf(desc, 256, L"%d,%d: %d / %f", x, y, (pix >> 24) & 0xFF, ((pix >> 24) & 0xFF) * (1.0f / 255.0f));
break;
@@ -410,6 +419,15 @@ void CGEDebugger::DescribePixel(u32 pix, GPUDebugBufferFormat fmt, int x, int y,
_snwprintf(desc, 256, L"%d,%d: %f / %f", x, y, *(float *)&pix, FromScaledDepth(*(float *)&pix));
break;
case GPU_DBG_FORMAT_FLOAT_DIV_256:
{
double z = *(float *)&pix;
int z24 = (int)(z * 16777215.0);
int z16 = z24 - 0x800000 + 0x8000;
_snwprintf(desc, 256, L"%d,%d: %d / %f", x, y, z16, (z - 0.5 + (1.0 / 512.0)) * 256.0);
}
break;
default:
_snwprintf(desc, 256, L"Unexpected format");
}
+21
View File
@@ -250,10 +250,18 @@ void SimpleGLWindow::Draw(const u8 *data, int w, int h, bool flipped, Format fmt
} else if (fmt == FORMAT_FLOAT) {
glfmt = GL_FLOAT;
components = GL_RED;
} else if (fmt == FORMAT_FLOAT_DIV_256) {
glfmt = GL_UNSIGNED_INT;
components = GL_RED;
finalData = Reformat(data, fmt, w * h);
} else if (fmt == FORMAT_24BIT_8X) {
glfmt = GL_UNSIGNED_INT;
components = GL_RED;
finalData = Reformat(data, fmt, w * h);
} else if (fmt == FORMAT_24BIT_8X_DIV_256) {
glfmt = GL_UNSIGNED_INT;
components = GL_RED;
finalData = Reformat(data, fmt, w * h);
} else if (fmt == FORMAT_24X_8BIT) {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glfmt = GL_UNSIGNED_BYTE;
@@ -545,6 +553,19 @@ const u8 *SimpleGLWindow::Reformat(const u8 *data, Format fmt, u32 numPixels) {
for (u32 i = 0; i < numPixels; ++i) {
reformatBuf_[i] = (data32[i] << 8) | ((data32[i] >> 16) & 0xFF);
}
} else if (fmt == FORMAT_24BIT_8X_DIV_256) {
for (u32 i = 0; i < numPixels; ++i) {
int z24 = data32[i] & 0x00FFFFFF;
int z16 = z24 - 0x800000 + 0x8000;
reformatBuf_[i] = (z16 << 16) | z16;
}
} else if (fmt == FORMAT_FLOAT_DIV_256) {
for (u32 i = 0; i < numPixels; ++i) {
double z = *(float *)&data32[i];
int z24 = (int)(z * 16777215.0);
int z16 = z24 - 0x800000 + 0x8000;
reformatBuf_[i] = (z16 << 16) | z16;
}
} else if (fmt == FORMAT_24X_8BIT) {
u8 *buf8 = (u8 *)reformatBuf_;
for (u32 i = 0; i < numPixels; ++i) {
+3
View File
@@ -42,6 +42,9 @@ struct SimpleGLWindow {
FORMAT_8BIT = 0x12,
FORMAT_24BIT_8X = 0x13,
FORMAT_24X_8BIT = 0x14,
FORMAT_FLOAT_DIV_256 = 0x18,
FORMAT_24BIT_8X_DIV_256 = 0x1B,
};
enum Flags {