From 26c945a31f12f9599ebd27de48c968a2b44c4e58 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Mon, 8 Oct 2012 23:15:56 +0000 Subject: [PATCH] gpu: allow to specify the pixel format when creating a texture; the internal format is then automatically deduced. Only a few 8-bit RGB or RGBA formats are supported for now. --- src/gpu/texture.cpp | 103 ++++++++++++++++++++++++++---------- src/gpu/texture.h | 19 ++++++- src/tileset.cpp | 112 ++++++---------------------------------- tutorial/11_fractal.cpp | 3 +- 4 files changed, 113 insertions(+), 124 deletions(-) diff --git a/src/gpu/texture.cpp b/src/gpu/texture.cpp index 28d5dcdc..b8982a8b 100644 --- a/src/gpu/texture.cpp +++ b/src/gpu/texture.cpp @@ -42,6 +42,7 @@ class TextureData friend class Texture; ivec2 m_size; + PixelFormat m_format; #if defined USE_D3D9 IDirect3DTexture9 *m_tex; @@ -49,6 +50,8 @@ class TextureData D3DTexture *m_tex; #else GLuint m_texid; + GLint m_internal_format; + GLenum m_gl_format, m_gl_type; #endif }; @@ -57,37 +60,85 @@ class TextureData // ----------------- // -/* FIXME: this is all hardcoded over the place */ -#if __CELLOS_LV2__ -static GLint const INTERNAL_FORMAT = GL_ARGB_SCE; -static GLenum const TEXTURE_FORMAT = GL_BGRA; -static GLenum const TEXTURE_TYPE = GL_UNSIGNED_INT_8_8_8_8_REV; -#elif defined __native_client__ || defined HAVE_GLES_2X -static GLint const INTERNAL_FORMAT = GL_RGBA; -static GLenum const TEXTURE_FORMAT = GL_RGBA; -static GLenum const TEXTURE_TYPE = GL_UNSIGNED_BYTE; -#elif !defined USE_D3D9 && !defined _XBOX -/* Seems efficient for little endian textures */ -static GLint const INTERNAL_FORMAT = GL_RGBA; -static GLenum const TEXTURE_FORMAT = GL_BGRA; -static GLenum const TEXTURE_TYPE = GL_UNSIGNED_INT_8_8_8_8_REV; -#endif +#define GET_CLAMPED(array, index) \ + array[std::max(0, std::min((int)(index), \ + (int)sizeof(array) / (int)sizeof(*array)))] -Texture::Texture(ivec2 size) +Texture::Texture(ivec2 size, PixelFormat format) : m_data(new TextureData) { m_data->m_size = size; + m_data->m_format = format; + +#if defined USE_D3D9 || defined _XBOX + static int const d3d_formats[] = + { + /* Unknown */ + D3DFMT_UNKNOWN, + + /* R8G8B8 */ + D3DFMT_R8G8B8, + + /* A8R8G8B8 */ +# if defined USE_D3D9 + D3DFMT_A8R8G8B8, +# else + /* By default the X360 will swizzle the texture. Ask for linear. */ + D3DFMT_LIN_A8R8G8B8, +# endif + }; + + int d3d_format = GET_CLAMPED(d3d_formats, format); +# if defined USE_D3D9 + int d3d_usage = D3DUSAGE_DYNAMIC; +# else + int d3d_usage = D3DUSAGE_WRITEONLY; +# endif -#if defined USE_D3D9 - g_d3ddevice->CreateTexture(m_data->m_size.x, m_data->m_size.y, 1, - D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, - D3DPOOL_DEFAULT, &m_data->m_tex, NULL); -#elif defined _XBOX - /* By default the X360 will swizzle the texture. Ask for linear. */ g_d3ddevice->CreateTexture(m_data->m_size.x, m_data->m_size.y, 1, - D3DUSAGE_WRITEONLY, D3DFMT_LIN_A8R8G8B8, + d3d_usage, d3d_format, D3DPOOL_DEFAULT, &m_data->m_tex, NULL); #else + static struct + { + GLint internal_format; + GLenum format, type; + } + const gl_formats[] = + { + /* Unknown */ + { 0, 0, 0 }, + + /* R8G8B8 */ + { GL_RGB, GL_RGB, GL_UNSIGNED_BYTE }, + + /* A8R8G8B8 */ +#if __CELLOS_LV2__ + { GL_ARGB_SCE, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV }, +#elif defined __native_client__ || defined HAVE_GLES_2X + { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, +#else + /* Seems efficient for little endian textures */ + { GL_RGBA, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV }, +#endif + + /* A8B8G8R8 */ +#if __CELLOS_LV2__ + { GL_ARGB_SCE, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV }, +#elif defined __native_client__ || defined HAVE_GLES_2X + /* FIXME: if GL_RGBA is not available, we should advertise + * this format as "not available" on this platform. */ + { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, +#else + /* Seems efficient for little endian textures */ + { GL_RGBA, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV }, +#endif + }; + + m_data->m_internal_format = GET_CLAMPED(gl_formats, format).internal_format; + m_data->m_gl_format = GET_CLAMPED(gl_formats, format).format; + m_data->m_gl_type = GET_CLAMPED(gl_formats, format).type; + glGenTextures(1, &m_data->m_texid); glBindTexture(GL_TEXTURE_2D, m_data->m_texid); @@ -129,9 +180,9 @@ void Texture::SetData(void *data) m_data->m_tex->UnlockRect(0); #else - glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, + glTexImage2D(GL_TEXTURE_2D, 0, m_data->m_internal_format, m_data->m_size.x, m_data->m_size.y, 0, - TEXTURE_FORMAT, TEXTURE_TYPE, data); + m_data->m_gl_format, m_data->m_gl_type, data); #endif } @@ -153,7 +204,7 @@ void Texture::SetSubData(ivec2 origin, ivec2 size, void *data) #else glTexSubImage2D(GL_TEXTURE_2D, 0, origin.x, origin.y, size.x, size.y, - TEXTURE_FORMAT, TEXTURE_TYPE, data); + m_data->m_gl_format, m_data->m_gl_type, data); #endif } diff --git a/src/gpu/texture.h b/src/gpu/texture.h index b9bb3332..214edea9 100644 --- a/src/gpu/texture.h +++ b/src/gpu/texture.h @@ -19,10 +19,27 @@ namespace lol { +struct PixelFormat +{ + /* XXX: make sure to update texture.cpp when this changes */ + enum Value + { + Unknown = 0, + R8G8B8, + A8R8G8B8, + A8B8G8R8, + } + m_value; + + inline PixelFormat() : m_value(Unknown) {} + inline PixelFormat(Value v) : m_value(v) {} + inline operator Value() { return m_value; } +}; + class Texture { public: - Texture(ivec2 size); + Texture(ivec2 size, PixelFormat format); ~Texture(); void Bind(); diff --git a/src/tileset.cpp b/src/tileset.cpp index df336509..ffbb9213 100644 --- a/src/tileset.cpp +++ b/src/tileset.cpp @@ -56,13 +56,7 @@ private: float tx, ty; Image *img; -#if defined USE_D3D9 - IDirect3DTexture9 *m_tex; -#elif defined _XBOX - D3DTexture *m_tex; -#else - GLuint m_tex; -#endif + Texture *m_texture; }; /* @@ -77,7 +71,7 @@ TileSet::TileSet(char const *path, ivec2 size, ivec2 count) sprintf(data->name, " %s", path); data->tiles = NULL; - data->m_tex = 0; + data->m_texture = 0; data->img = new Image(path); data->isize = data->img->GetSize(); @@ -119,46 +113,24 @@ void TileSet::TickDraw(float seconds) if (data->img) delete data->img; else -#if defined USE_D3D9 || defined _XBOX - /* FIXME: is it really the correct call? */ - data->m_tex->Release(); -#else - glDeleteTextures(1, &data->m_tex); -#endif + delete data->m_texture; } else if (data->img) { -#if defined USE_D3D9 || defined _XBOX - D3DFORMAT format; -#else - GLuint format; -#endif int planes; + PixelFormat format = PixelFormat::Unknown; switch (data->img->GetFormat()) { case Image::FORMAT_RGB: -#if defined USE_D3D9 - format = D3DFMT_R8G8B8; -#elif defined _XBOX - format = D3DFMT_LIN_A8R8G8B8; /* FIXME */ -#else - format = GL_RGB; -#endif - planes = 3; - break; + format = PixelFormat::R8G8B8; + planes = 3; + break; case Image::FORMAT_RGBA: default: -#if defined USE_D3D9 - format = D3DFMT_A8R8G8B8; -#elif defined _XBOX - /* By default the X360 will swizzle the texture. Ask for linear. */ - format = D3DFMT_LIN_A8R8G8B8; -#else - format = GL_RGBA; -#endif - planes = 4; - break; + format = PixelFormat::A8R8G8B8; + planes = 4; + break; } int w = PotUp(data->isize.x); @@ -175,41 +147,8 @@ void TileSet::TickDraw(float seconds) pixels = tmp; } -#if defined USE_D3D9 || defined _XBOX - D3DLOCKED_RECT rect; - HRESULT hr; -# if defined USE_D3D9 - hr = g_d3ddevice->CreateTexture(w, h, 1, D3DUSAGE_DYNAMIC, format, - D3DPOOL_DEFAULT, &data->m_tex, NULL); -# elif defined _XBOX - hr = g_d3ddevice->CreateTexture(w, h, 1, D3DUSAGE_WRITEONLY, format, - D3DPOOL_DEFAULT, &data->m_tex, NULL); -# endif - if (FAILED(hr)) - Abort(); -# if defined USE_D3D9 - hr = data->m_tex->LockRect(0, &rect, NULL, D3DLOCK_DISCARD); -# else - hr = data->m_tex->LockRect(0, &rect, NULL, 0); -# endif - if (FAILED(hr)) - Abort(); - for (int j = 0; j < h; j++) - memcpy((uint8_t *)rect.pBits + j * rect.Pitch, pixels + w * j * 4, w * 4); - hr = data->m_tex->UnlockRect(0); - if (FAILED(hr)) - Abort(); -#else - glGenTextures(1, &data->m_tex); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, data->m_tex); - - glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, - format, GL_UNSIGNED_BYTE, pixels); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); -#endif + data->m_texture = new Texture(ivec2(w, h), format); + data->m_texture->SetData(pixels); if (pixels != data->img->GetData()) free(pixels); @@ -237,32 +176,13 @@ ivec2 TileSet::GetSize(int tileid) const void TileSet::Bind() { - if (!data->img && data->m_tex) - { -#if defined USE_D3D9 || defined _XBOX - HRESULT hr = g_d3ddevice->SetTexture(0, data->m_tex); - if (FAILED(hr)) - Abort(); -#else - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, data->m_tex); -#endif - } + if (!data->img && data->m_texture) + data->m_texture->Bind(); } void TileSet::Unbind() { - if (!data->img && data->m_tex) - { -#if defined USE_D3D9 || defined _XBOX - HRESULT hr = g_d3ddevice->SetTexture(0, NULL); - if (FAILED(hr)) - Abort(); -#else - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); -#endif - } + ; } void TileSet::BlitTile(uint32_t id, vec3 pos, int o, vec2 scale, @@ -287,7 +207,7 @@ void TileSet::BlitTile(uint32_t id, vec3 pos, int o, vec2 scale, dtx = -dtx; } - if (!data->img && data->m_tex) + if (!data->img && data->m_texture) { float tmp[10]; diff --git a/tutorial/11_fractal.cpp b/tutorial/11_fractal.cpp index eb8394c4..7fd16a01 100644 --- a/tutorial/11_fractal.cpp +++ b/tutorial/11_fractal.cpp @@ -445,7 +445,8 @@ public: { /* Create a texture of half the width and twice the height * so that we can upload four different subimages each frame. */ - m_texture = new Texture(ivec2(m_size.x / 2, m_size.y * 2)); + m_texture = new Texture(ivec2(m_size.x / 2, m_size.y * 2), + PixelFormat::A8B8G8R8); /* Ensure the texture data is complete at least once, otherwise * uploading subimages will not work. */