| Name |
| |
| EXT_texture_shared_exponent |
| |
| Name Strings |
| |
| GL_EXT_texture_shared_exponent |
| |
| Contact |
| |
| Mark J. Kilgard, NVIDIA Corporation (mjk 'at' nvidia.com) |
| |
| Contributors |
| |
| Pat Brown, NVIDIA |
| Jon Leech |
| Bruce Merry, ARM |
| |
| Status |
| |
| Shipping |
| |
| Version |
| |
| Date: July 18, 2008 |
| Revision: 1.0 |
| |
| Number |
| |
| 333 |
| |
| Dependencies |
| |
| OpenGL 1.1 required |
| |
| ARB_color_buffer_float affects this extension. |
| |
| EXT_framebuffer_object affects this extension. |
| |
| This extension is written against the OpenGL 2.0 (September 7, |
| 2004) specification. |
| |
| Overview |
| |
| Existing texture formats provide either fixed-point formats with |
| limited range and precision but with compact encodings (allowing 32 |
| or fewer bits per multi-component texel), or floating-point formats |
| with tremendous range and precision but without compact encodings |
| (typically 16 or 32 bits per component). |
| |
| This extension adds a new packed format and new internal texture |
| format for encoding 3-component vectors (typically RGB colors) with |
| a single 5-bit exponent (biased up by 15) and three 9-bit mantissas |
| for each respective component. There is no sign bit so all three |
| components must be non-negative. The fractional mantissas are |
| stored without an implied 1 to the left of the decimal point. |
| Neither infinity nor not-a-number (NaN) are representable in this |
| shared exponent format. |
| |
| This 32 bits/texel shared exponent format is particularly well-suited |
| to high dynamic range (HDR) applications where light intensity is |
| typically stored as non-negative red, green, and blue components |
| with considerable range. |
| |
| New Procedures and Functions |
| |
| None |
| |
| New Tokens |
| |
| Accepted by the <internalformat> parameter of TexImage1D, |
| TexImage2D, TexImage3D, CopyTexImage1D, CopyTexImage2D, and |
| RenderbufferStorageEXT: |
| |
| RGB9_E5_EXT 0x8C3D |
| |
| Accepted by the <type> parameter of DrawPixels, ReadPixels, |
| TexImage1D, TexImage2D, GetTexImage, TexImage3D, TexSubImage1D, |
| TexSubImage2D, TexSubImage3D, GetHistogram, GetMinmax, |
| ConvolutionFilter1D, ConvolutionFilter2D, ConvolutionFilter3D, |
| GetConvolutionFilter, SeparableFilter2D, GetSeparableFilter, |
| ColorTable, ColorSubTable, and GetColorTable: |
| |
| UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E |
| |
| Accepted by the <pname> parameter of GetTexLevelParameterfv and |
| GetTexLevelParameteriv: |
| |
| TEXTURE_SHARED_SIZE_EXT 0x8C3F |
| |
| Additions to Chapter 2 of the 2.0 Specification (OpenGL Operation) |
| |
| None |
| |
| Additions to Chapter 3 of the 2.0 Specification (Rasterization) |
| |
| -- Section 3.6.4, Rasterization of Pixel Rectangles |
| |
| Add a new row to Table 3.5 (page 128): |
| |
| type Parameter Corresponding Special |
| Token Name GL Data Type Interpretation |
| ----------------------------- ------------- -------------- |
| UNSIGNED_INT_5_9_9_9_REV_EXT uint yes |
| |
| Add a new row to table 3.8: Packed pixel formats (page 132): |
| |
| type Parameter GL Data Number of Matching |
| Token Name Type Components Pixel Formats |
| ----------------------------- ------- ---------- ------------- |
| UNSIGNED_INT_5_9_9_9_REV_EXT uint 4 RGB |
| |
| Add a new entry to table 3.11: UNSIGNED_INT formats (page 134): |
| |
| UNSIGNED_INT_5_9_9_9_REV_EXT: |
| |
| 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 |
| +-------------+--------------------------+---------------------------+--------------------------+ |
| | 4th | 3rd | 2nd | 1st | |
| +-------------+--------------------------+---------------------------+--------------------------+ |
| |
| Add to the end of the 2nd paragraph starting "Pixels are draw using": |
| |
| "If type is UNSIGNED_INT_5_9_9_9_REV_EXT and format is not RGB then |
| the error INVALID_ENUM occurs." |
| |
| Add UNSIGNED_INT_5_9_9_9_REV_EXT to the list of packed formats in |
| the 10th paragraph after the "Packing" subsection (page 130). |
| |
| Add before the 3rd paragraph (page 135, starting "Calling DrawPixels |
| with a type of BITMAP...") from the end of the "Packing" subsection: |
| |
| "Calling DrawPixels with a type of UNSIGNED_INT_5_9_9_9_REV_EXT and |
| format of RGB is a special case in which the data are a series of GL |
| uint values. Each uint value specifies 4 packed components as shown |
| in table 3.11. The 1st, 2nd, 3rd, and 4th components are called |
| p_red, p_green, p_blue, and p_exp respectively and are treated as |
| unsigned integers. These are then used to compute floating-point |
| RGB components (ignoring the "Conversion to floating-point" section |
| below in this case) as follows: |
| |
| red = p_red * 2^(p_exp - B - N) |
| green = p_green * 2^(p_exp - B - N) |
| blue = p_blue * 2^(p_exp - B - N) |
| |
| where B is 15 (the exponent bias) and N is 9 (the number of mantissa |
| bits)." |
| |
| -- Section 3.8.1, Texture Image Specification: |
| |
| "Alternatively if the internalformat is RGB9_E5_EXT, the red, green, |
| and blue bits are converted to a shared exponent format according |
| to the following procedure: |
| |
| Components red, green, and blue are first clamped (in the process, |
| mapping NaN to zero) so: |
| |
| red_c = max(0, min(sharedexp_max, red)) |
| green_c = max(0, min(sharedexp_max, green)) |
| blue_c = max(0, min(sharedexp_max, blue)) |
| |
| where sharedexp_max is (2^N-1)/2^N * 2^(Emax-B), N is the number |
| of mantissa bits per component, Emax is the maximum allowed biased |
| exponent value (careful: not necessarily 2^E-1 when E is the number of |
| exponent bits), bits, and B is the exponent bias. For the RGB9_E5_EXT |
| format, N=9, Emax=31, and B=15. |
| |
| The largest clamped component, max_c, is determined: |
| |
| max_c = max(red_c, green_c, blue_c) |
| |
| A preliminary shared exponent is computed: |
| |
| exp_shared_p = max(-B-1, floor(log2(max_c))) + 1 + B |
| |
| A refined shared exponent is then computed as: |
| |
| max_s = floor(max_c / 2^(exp_shared_p - B - N) + 0.5) |
| |
| { exp_shared_p, 0 <= max_s < 2^N |
| exp_shared = { |
| { exp_shared_p+1, max_s == 2^N |
| |
| These integers values in the range 0 to 2^N-1 are then computed: |
| |
| red_s = floor(red_c / 2^(exp_shared - B - N) + 0.5) |
| green_s = floor(green_c / 2^(exp_shared - B - N) + 0.5) |
| blue_s = floor(blue_c / 2^(exp_shared - B - N) + 0.5) |
| |
| Then red_s, green_s, and blue_s are stored along with exp_shared in |
| the red, green, blue, and shared bits respectively of the texture |
| image. |
| |
| An implementation accepting pixel data of type |
| UNSIGNED_INT_5_9_9_9_REV_EXT with a format of RGB is allowed to store |
| the components "as is" if the implementation can determine the current |
| pixel transfer state act as an identity transform on the components." |
| |
| Add a new row and the "shared bits" column (blank for all existing |
| rows) to Table 3.16 (page 154). |
| |
| Sized Base R G B A L I D shared |
| Internal Format Internal Format bits bits bits bits bits bits bits bits |
| --------------------- --------------- ---- ---- ---- ---- ---- ---- ---- ------ |
| RGB9_E5_EXT RGB 9 9 9 5 |
| |
| -- Section 3.8.x, Shared Exponent Texture Color Conversion |
| |
| Insert this section AFTER section 3.8.14 Texture Comparison Modes |
| and BEFORE section 3.8.15 Texture Application (and after the "sRGB |
| Texture Color Conversion" if EXT_texture_sRGB is supported). |
| |
| "If the currently bound texture's internal format is RGB9_E5_EXT, the |
| red, green, blue, and shared bits are converted to color components |
| (prior to filtering) using the following shared exponent decoding. |
| |
| The components red_s, green_s, blue_s, and exp_shared values (see |
| section 3.8.1) are treated as unsigned integers and are converted |
| to red, green, blue as follows: |
| |
| red = red_s * 2^(exp_shared - B) |
| green = green_s * 2^(exp_shared - B) |
| blue = blue_s * 2^(exp_shared - B)" |
| |
| Additions to Chapter 4 of the 2.0 Specification (Per-Fragment Operations |
| and the Frame Buffer) |
| |
| -- Section 4.3.2, Reading Pixels |
| |
| Add a row to table 4.7 (page 224); |
| |
| Component |
| type Parameter GL Data Type Conversion Formula |
| ----------------------------- ------------ ------------------ |
| UNSIGNED_INT_5_9_9_9_REV_EXT uint special |
| |
| Replace second paragraph of "Final Conversion" (page 222) to read: |
| |
| For an RGBA color, if <type> is not FLOAT or |
| UNSIGNED_INT_5_9_9_9_REV_EXT, or if the CLAMP_READ_COLOR_ARB is |
| TRUE, or CLAMP_READ_COLOR_ARB is FIXED_ONLY_ARB and the selected |
| color (or texture) buffer is a fixed-point buffer, each component |
| is first clamped to [0,1]. Then the appropriate conversion formula |
| from table 4.7 is applied the component. |
| |
| In the special case when calling ReadPixels with a type of |
| UNSIGNED_INT_5_9_9_9_REV_EXT and format of RGB, the conversion |
| is done as follows: The returned data are packed into a series of |
| GL uint values. The red, green, and blue components are converted |
| to red_s, green_s, blue_s, and exp_shared integers as described in |
| section 3.8.1 when the internalformat is RGB9_E5_EXT. The red_s, |
| green_s, blue_s, and exp_shared are then packed as the 1st, 2nd, |
| 3rd, and 4th components of the UNSIGNED_INT_5_9_9_9_REV_EXT format |
| as shown in table 3.11." |
| |
| Additions to Chapter 5 of the 2.0 Specification (Special Functions) |
| |
| None |
| |
| Additions to Chapter 6 of the 2.0 Specification (State and State Requests) |
| |
| -- Section 6.1.3, Enumerated Queries |
| |
| Add TEXTURE_SHARED_SIZE_EXT to the list of queries in the first |
| sentence of the fifth paragraph (page 247) so it reads: |
| |
| "For texture images with uncompressed internal formats, queries of |
| value of TEXTURE_RED_SIZE, TEXTURE_GREEN_SIZE, TEXTURE_BLUE_SIZE, |
| TEXTURE_ALPHA_SIZE, TEXTURE_LUMINANCE_SIZE, TEXTURE_DEPTH_SIZE, |
| TEXTURE_SHARED_SIZE_EXTT, and TEXTURE_INTENSITY_SIZE return the |
| actual resolutions of the stored image array components, not the |
| resolutions specified when the image array was defined." |
| |
| Additions to the OpenGL Shading Language specification |
| |
| None |
| |
| Additions to the GLX Specification |
| |
| None |
| |
| GLX Protocol |
| |
| None. |
| |
| Dependencies on ARB_color_buffer_float |
| |
| If ARB_color_buffer_float is not supported, replace this amended |
| sentence from 4.3.2 above |
| |
| "For an RGBA color, if <type> is not FLOAT or |
| UNSIGNED_INT_5_9_9_9_REV_EXT, or if the CLAMP_READ_COLOR_ARB is TRUE, or |
| CLAMP_READ_COLOR_ARB is FIXED_ONLY_ARB and the selected color buffer |
| (or texture image for GetTexImage) is a fixed-point buffer (or texture |
| image for GetTexImage), each component is first clamped to [0,1]." |
| |
| with |
| |
| "For an RGBA color, if <type> is not FLOAT or |
| UNSIGNED_INT_5_9_9_9_REV_EXT and the selected color buffer (or |
| texture image for GetTexImage) is a fixed-point buffer (or texture |
| image for GetTexImage), each component is first clamped to [0,1]." |
| |
| Dependencies on EXT_framebuffer_object |
| |
| If EXT_framebuffer_object is not supported, then |
| RenderbufferStorageEXT is not supported and the RGB9_E5_EXT |
| internalformat is therefore not supported by RenderbufferStorageEXT. |
| |
| Errors |
| |
| Relaxation of INVALID_ENUM errors |
| --------------------------------- |
| |
| TexImage1D, TexImage2D, TexImage3D, CopyTexImage1D, CopyTexImage2D, |
| and RenderbufferStorageEXT accept the new RGB9_E5_EXT token for |
| internalformat. |
| |
| DrawPixels, ReadPixels, TexImage1D, TexImage2D, GetTexImage, |
| TexImage3D, TexSubImage1D, TexSubImage2D, TexSubImage3D, |
| GetHistogram, GetMinmax, ConvolutionFilter1D, ConvolutionFilter2D, |
| ConvolutionFilter3D, GetConvolutionFilter, SeparableFilter2D, |
| GetSeparableFilter, ColorTable, ColorSubTable, and GetColorTable |
| accept the new UNSIGNED_INT_5_9_9_9_REV_EXT token for type. |
| |
| GetTexLevelParameterfv and GetTexLevelParameteriv accept the new |
| TEXTURE_SHARED_SIZE_EXT token for <pname>. |
| |
| New errors |
| ---------- |
| |
| INVALID_OPERATION is generated by DrawPixels, ReadPixels, TexImage1D, |
| TexImage2D, GetTexImage, TexImage3D, TexSubImage1D, TexSubImage2D, |
| TexSubImage3D, GetHistogram, GetMinmax, ConvolutionFilter1D, |
| ConvolutionFilter2D, ConvolutionFilter3D, GetConvolutionFilter, |
| SeparableFilter2D, GetSeparableFilter, ColorTable, ColorSubTable, |
| and GetColorTable if <type> is UNSIGNED_INT_5_9_9_9_REV_EXT |
| and <format> is not RGB. |
| |
| New State |
| |
| In table 6.17, Textures (page 278), increment the 42 in "n x Z42*" |
| by 1 for the RGB9_E5_EXT format. |
| |
| [NOTE: The OpenGL 2.0 specification actually should read "n x Z48*" |
| because of the 6 generic compressed internal formats in table 3.18.] |
| |
| Add the following entry to table 6.17: |
| |
| Get Value Type Get Command Value Description Sec. Attribute |
| ----------------------- ------ -------------------- ------- ------------------------------------ ---- --------- |
| TEXTURE_SHARED_SIZE_EXT n x Z+ GetTexLevelParameter 0 xD texture image i's shared exponent 3.8 - |
| field size |
| |
| New Implementation Dependent State |
| |
| None |
| |
| Appendix |
| |
| This source code provides ANSI C routines. It assumes the C "float" |
| data type is stored with the IEEE 754 32-bit floating-point format. |
| Make sure you define __LITTLE_ENDIAN or __BIG_ENDIAN appropriate |
| for your target system. |
| |
| XXX: code below not tested on big-endian platform... |
| |
| ------------------- start of source code ------------------------ |
| |
| #include <assert.h> |
| #include <math.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #define __LITTLE_ENDIAN 1 |
| #define __BIG_ENDIAN 2 |
| |
| #ifdef _WIN32 |
| #define __BYTE_ORDER __LITTLE_ENDIAN |
| #endif |
| |
| #define RGB9E5_EXPONENT_BITS 5 |
| #define RGB9E5_MANTISSA_BITS 9 |
| #define RGB9E5_EXP_BIAS 15 |
| #define RGB9E5_MAX_VALID_BIASED_EXP 31 |
| |
| #define MAX_RGB9E5_EXP (RGB9E5_MAX_VALID_BIASED_EXP - RGB9E5_EXP_BIAS) |
| #define RGB9E5_MANTISSA_VALUES (1<<RGB9E5_MANTISSA_BITS) |
| #define MAX_RGB9E5_MANTISSA (RGB9E5_MANTISSA_VALUES-1) |
| #define MAX_RGB9E5 (((float)MAX_RGB9E5_MANTISSA)/RGB9E5_MANTISSA_VALUES * (1<<MAX_RGB9E5_EXP)) |
| #define EPSILON_RGB9E5 ((1.0/RGB9E5_MANTISSA_VALUES) / (1<<RGB9E5_EXP_BIAS)) |
| |
| typedef struct { |
| #ifdef __BYTE_ORDER |
| #if __BYTE_ORDER == __BIG_ENDIAN |
| unsigned int negative:1; |
| unsigned int biasedexponent:8; |
| unsigned int mantissa:23; |
| #elif __BYTE_ORDER == __LITTLE_ENDIAN |
| unsigned int mantissa:23; |
| unsigned int biasedexponent:8; |
| unsigned int negative:1; |
| #endif |
| #endif |
| } BitsOfIEEE754; |
| |
| typedef union { |
| unsigned int raw; |
| float value; |
| BitsOfIEEE754 field; |
| } float754; |
| |
| typedef struct { |
| #ifdef __BYTE_ORDER |
| #if __BYTE_ORDER == __BIG_ENDIAN |
| unsigned int biasedexponent:RGB9E5_EXPONENT_BITS; |
| unsigned int b:RGB9E5_MANTISSA_BITS; |
| unsigned int g:RGB9E5_MANTISSA_BITS; |
| unsigned int r:RGB9E5_MANTISSA_BITS; |
| #elif __BYTE_ORDER == __LITTLE_ENDIAN |
| unsigned int r:RGB9E5_MANTISSA_BITS; |
| unsigned int g:RGB9E5_MANTISSA_BITS; |
| unsigned int b:RGB9E5_MANTISSA_BITS; |
| unsigned int biasedexponent:RGB9E5_EXPONENT_BITS; |
| #endif |
| #endif |
| } BitsOfRGB9E5; |
| |
| typedef union { |
| unsigned int raw; |
| BitsOfRGB9E5 field; |
| } rgb9e5; |
| |
| float ClampRange_for_rgb9e5(float x) |
| { |
| if (x > 0.0) { |
| if (x >= MAX_RGB9E5) { |
| return MAX_RGB9E5; |
| } else { |
| return x; |
| } |
| } else { |
| /* NaN gets here too since comparisons with NaN always fail! */ |
| return 0.0; |
| } |
| } |
| |
| float MaxOf3(float x, float y, float z) |
| { |
| if (x > y) { |
| if (x > z) { |
| return x; |
| } else { |
| return z; |
| } |
| } else { |
| if (y > z) { |
| return y; |
| } else { |
| return z; |
| } |
| } |
| } |
| |
| /* Ok, FloorLog2 is not correct for the denorm and zero values, but we |
| are going to do a max of this value with the minimum rgb9e5 exponent |
| that will hide these problem cases. */ |
| int FloorLog2(float x) |
| { |
| float754 f; |
| |
| f.value = x; |
| return (f.field.biasedexponent - 127); |
| } |
| |
| int Max(int x, int y) |
| { |
| if (x > y) { |
| return x; |
| } else { |
| return y; |
| } |
| } |
| |
| rgb9e5 float3_to_rgb9e5(const float rgb[3]) |
| { |
| rgb9e5 retval; |
| float maxrgb; |
| int rm, gm, bm; |
| float rc, gc, bc; |
| int exp_shared; |
| double denom; |
| |
| rc = ClampRange_for_rgb9e5(rgb[0]); |
| gc = ClampRange_for_rgb9e5(rgb[1]); |
| bc = ClampRange_for_rgb9e5(rgb[2]); |
| |
| maxrgb = MaxOf3(rc, gc, bc); |
| exp_shared = Max(-RGB9E5_EXP_BIAS-1, FloorLog2(maxrgb)) + 1 + RGB9E5_EXP_BIAS; |
| assert(exp_shared <= RGB9E5_MAX_VALID_BIASED_EXP); |
| assert(exp_shared >= 0); |
| /* This pow function could be replaced by a table. */ |
| denom = pow(2, exp_shared - RGB9E5_EXP_BIAS - RGB9E5_MANTISSA_BITS); |
| |
| maxm = (int) floor(maxrgb / denom + 0.5); |
| if (maxm == MAX_RGB9E5_MANTISSA+1) { |
| denom *= 2; |
| exp_shared += 1; |
| assert(exp_shared <= RGB9E5_MAX_VALID_BIASED_EXP); |
| } else { |
| assert(maxm <= MAX_RGB9E5_MANTISSA); |
| } |
| |
| rm = (int) floor(rc / denom + 0.5); |
| gm = (int) floor(gc / denom + 0.5); |
| bm = (int) floor(bc / denom + 0.5); |
| |
| assert(rm <= MAX_RGB9E5_MANTISSA); |
| assert(gm <= MAX_RGB9E5_MANTISSA); |
| assert(bm <= MAX_RGB9E5_MANTISSA); |
| assert(rm >= 0); |
| assert(gm >= 0); |
| assert(bm >= 0); |
| |
| retval.field.r = rm; |
| retval.field.g = gm; |
| retval.field.b = bm; |
| retval.field.biasedexponent = exp_shared; |
| |
| return retval; |
| } |
| |
| void rgb9e5_to_float3(rgb9e5 v, float retval[3]) |
| { |
| int exponent = v.field.biasedexponent - RGB9E5_EXP_BIAS - RGB9E5_MANTISSA_BITS; |
| float scale = (float) pow(2, exponent); |
| |
| retval[0] = v.field.r * scale; |
| retval[1] = v.field.g * scale; |
| retval[2] = v.field.b * scale; |
| } |
| |
| ------------------- end of source code ------------------------ |
| |
| Issues |
| |
| 1) What should this extension be called? |
| |
| RESOLVED: EXT_texture_shared_exponent |
| |
| The "EXT_texture" part indicates the extension is in the texture |
| domain and "shared_exponent" indicates the extension is adding |
| a new shared exponent formats. |
| |
| EXT_texture_rgb9e5 was considered but there's no precedent for |
| extension names to be so explicit (or cryptic?) about format |
| specifics in the extension name. |
| |
| 2) There are many possible encodings for a shared exponent format. |
| Which encoding does this extension specify? |
| |
| RESOLVED: A single 5-bit exponent stored as an unsigned |
| value biased by 15 and three 9-bit mantissas for each of 3 |
| components. There are no sign bits so all three components |
| must be non-negative. The fractional mantissas assume an implied |
| 0 left of the decimal point because having an implied leading |
| 1 is inconsistent with sharing the exponent. Neither Infinity |
| nor Not-a-Number (NaN) are representable in this shared exponent |
| format. |
| |
| We chose this format because it closely matches the range and |
| precision of the s10e5 half-precision floating-point described |
| in the ARB_half_float_pixel and ARB_texture_float specifications. |
| |
| 3) Why not an 8-bit shared exponent? |
| |
| RESOLVED: Greg Ward's RGBE shared exponent encoding uses an |
| 8-bit exponent (same as a single-precision IEEE value) but we |
| believe the rgb9e5 is more generally useful than rgb8e8. |
| |
| An 8-bit exponent provides far more range than is typically |
| required for graphics applications. However, an extra bit |
| of precision for each component helps in situations where a |
| high magnitude component dominates a low magnitude component. |
| Having an 8-bit shared exponent and 8-bit mantissas are amenable |
| to CPUs that facilitate 8-bit sized reads and writes over non-byte |
| aligned fields, but GPUs do not suffer from this issue. |
| |
| Indeed GPUs with s10e5 texture filtering can use that same |
| filtering hardware for rgb9e5 textures. |
| |
| However, future extensions could add other shared exponent formats |
| so we name the tokens to indicate the |
| |
| 4) Should there be an external format and type for rgb9e5? |
| |
| RESOLVED: Yes, hence the external format GL_RGB9_E5_EXT and |
| type GL_UNSIGNED_INT_5_9_9_9_REV_EXT. This makes it fast to load |
| GL_RGB9_E5_EXT textures without any translation by the driver. |
| |
| 5) Why is the exponent bias 15? |
| |
| RESOLVED: The best technical choice of 15. Hopefully, this |
| discussion sheds insight into the numerics of the shared exponent |
| format in general. |
| |
| With conventional floating-point formats, the number corresponding |
| to a finite, non-denorm, non-zero floating-point value is |
| |
| value = -1^sgn * 2^(exp-bias) * 1.frac |
| |
| where sgn is the sign bit (so 1 for sgn negative because -1^-1 |
| == -1 and 0 means positive because -1^0 == +1), exp is an |
| (unsigned) BIASED exponent and bias is the format's constant bias |
| to subtract to get the unbiased (possibly negative) exponent; |
| and frac is the fractional portion of the mantissa with the |
| "1." indicating an implied leading 1. |
| |
| An exp value of zero indicates so-called denormalized values |
| (denorms). With conventional floating-point formats, the number |
| corresponding to a denorm floating-point value is |
| |
| value = -1^sgn * 2^(exp-bias+1) * 0.frac |
| |
| where the only difference between the denorm and non-denorm case |
| is the bias is one greater in the denorm case and the implied |
| leading digit is a zero instead of a one. |
| |
| Ideally, the rgb9e5 shared exponent format would represent |
| roughly the same range of finite values as the s10e5 format |
| specified by the ARB_texture_float extension. The s10e5 format |
| has an exponent bias of 15. |
| |
| While conventional floating-point formats cleverly use an implied |
| leading 1 for non-denorm, finite values, a shared exponent format |
| cannot use an implied leading 1 because each component may have |
| a different magnitude for its most-significant binary digit. |
| The implied leading 1 assumes we have the flexibility to adjust |
| the mantissa and exponent together to ensure an implied leading 1. |
| That flexibility is not present when the exponent is shared. |
| |
| So the rgb9e5 format cannot assume an implied leading one. |
| Instead, an implied leading zero is assumed (much like the |
| conventional denorm case). |
| |
| The rgb9e5 format eliminate support representing negative, |
| Infinite, not-a-number (NaN), and denorm values. |
| |
| We've already discussed how the BIASED zero exponent is used to |
| encode denorm values (and zero) with conventional floating-point |
| formats. The largest BIASED exponent (31 for s10e5, 127 for |
| s23e8) for conventional floating-point fomats indicates Infinity |
| and NaN values. This means these two extrema exponent values are |
| "off limits" for run-of-the-mill values. |
| |
| The numbers corresponding to a shared exponent format value are: |
| |
| value_r = 2^(exp-bias) * 0.frac_r |
| value_g = 2^(exp-bias) * 0.frac_g |
| value_b = 2^(exp-bias) * 0.frac_b |
| |
| where there is no sgn since all values are non-negative, exp is |
| the (unsigned) BIASED exponent and bias is the format's constant |
| bias to subtract to get the unbiased (possibly negative) exponent; |
| and frac_r, frac_g, and frac_b are the fractional portion of |
| the mantissas of the r, g, and b components respectively with |
| "0." indicating an implied leading 0. |
| |
| There should be no "off limits" exponents for the shared exponent |
| format since there is no requirement for representing Infinity |
| or NaN values and denorm is not a special case. Because of |
| the implied leading zero, any component with all zeros for its |
| mantissa is zero, no matter the shared exponent's value. |
| |
| So the run-of-the-mill BIASED range of exponents for s10e5 is |
| 1 to 30. But the rgb9e5 shared exponent format consistently |
| uses the same rule for all exponents from 0 to 31. |
| |
| What exponent bias best allows us to represent the range of |
| s10e5 with the rgb9e5 format? 15. |
| |
| Consider the maximum representable finite s10e5 magnitude. |
| The exponent would be 30 (31 would encode an Infinite or NaN |
| value) and the binary mantissa would be 1 followed by ten |
| fractional 1's. Effectively: |
| |
| s10e5_max = 1.1111111111 * 2^(30-15) |
| = 1.1111111111 * 2^15 |
| |
| For an rgb9e5 value with a bias of 15, the largest representable |
| value is: |
| |
| rgb9e5_max = 0.111111111 * 2^(31-15) |
| = 0.111111111 * 2^16 |
| = 1.11111111 * 2^15 |
| |
| If you ignore two LSBs, these values are nearly identical. |
| The rgb9e5_max value is exactly representable as an s10e5 value. |
| |
| For an rgb9e5 value with a bias of 15, the smallest non-zero |
| representable value is: |
| |
| rgb9e5_min = 0.000000001 * 2^(0-15) |
| rgb9e5_min = 0.000000001 * 2^-15 |
| rgb9e5_min = 0.0000000001 * 2^-14 |
| |
| So the s10e5_min and rgb9e5_min values exactly match (of course, |
| this assumes the shared exponent bias is 15 which might not be |
| the case if other components demand higher exponents). |
| |
| 8) Should there be an rgb9e5 framebuffer format? |
| |
| RESOLVED: No. Rendering to rgb9e5 is better left to another |
| extension and would require the hardware to convert from a |
| (floating-point) RGBA value into an rgb9e5 encoding. |
| |
| Interactions with EXT_framebuffer_object are specified, |
| but the expectation is this is not a renderable |
| format and glCheckFramebufferStatusEXT would return |
| GL_FRAMEBUFFER_UNSUPPORTED_EXT. |
| |
| An implementation certainly could make this texture internal |
| format renderable when used with a framebuffer object. Note that |
| the shared exponent means masked components may be lossy in |
| their masking. For example, a very small but non-zero value in |
| a masked component could get flushed to zero if a large enough |
| value is written into an unmasked component. |
| |
| 9) Should automatic mipmap generation be supported for rgb9e5 |
| textures? |
| |
| RESOLVED: Yes. |
| |
| 10) Should non-texture and non-framebuffer commands for loading |
| pixel data accept the GL_UNSIGNED_INT_5_9_9_9_REV_EXT type? |
| |
| RESOLVED: Yes. |
| |
| Once the pixel path has to support the new type/format combination |
| of GL_UNSIGNED_INT_5_9_9_9_REV_EXT / GL_RGB for specifying and |
| querying texture images, it might as well be supported for all |
| commands that pack and unpack RGB pixel data. |
| |
| The specification is written such that the glDrawPixels |
| type/format parameters are accepted by glReadPixels, |
| glTexGetImage, glTexImage2D, and other commands that are specified |
| in terms of glDrawPixels. |
| |
| 11) Should non-texture internal formats (such as for color tables, |
| convolution kernels, histogram bins, and min/max tables) accept |
| GL_RGB9_E5_EXT format? |
| |
| RESOLVED: No. |
| |
| That's pointless. No hardware is ever likely to support |
| GL_RGB9_E5_EXT internalformats for anything other than textures |
| and maybe color buffers in the future. This format is not |
| interesting for color tables, convolution kernels, etc. |
| |
| 12) Should a format be supported with sign bits for each component? |
| |
| RESOLVED: No. |
| |
| An srgb8e5 format with a sign bit per component could be useful |
| but is better left to another extension. |
| |
| 13) The rgb9e5 allows two 32-bit values encoded as rgb9e5 to |
| correspond to the exact same 3 components when expanded to |
| floating-point. Is this a problem? |
| |
| RESOLVED: No, there's no problem here. |
| |
| An encoder is likely to always pack components so at least |
| one mantissa will have an explicit leading one, but there's no |
| requirement for that. |
| |
| Applications might be able to take advantage of this by quickly |
| dividing all three components by a power-of-two by simply |
| subtracting log2 of the power-of-two from the shared exponent (as |
| long as the exponent is greater than zero prior to the subtract). |
| |
| Arguably, the shared exponent format could maintain a slight |
| amount of extra precision (one bit per mantissa) if the format |
| said if the most significant bits of all three mantissas are |
| either all one or all zero and the biased shared exponent was not |
| zero, then an implied leading 1 should be assumed and the shared |
| exponent should be treated as one smaller than it really is. |
| While this would preserve an extra least-significant bit of |
| mantissa precision for components of approximately the same |
| magnitude, it would complicate the encoding and decoding of |
| shared exponent values. |
| |
| 14) Can you provide some C code for encoding three floating-point |
| values into the rgb9e5 format? |
| |
| RESOLVED: Sure. See the Appendix. |
| |
| 15) Should we support a non-REV version of the |
| GL_UNSIGNED_INT_5_9_9_9_REV_EXT token? |
| |
| RESOLVED: No. The shared exponent is always the 5 most |
| significant bits of the 32 bit word. The first (red) mantissa |
| is in the least significant 9 bits, followed by 9 bits for the |
| second (green) mantissa, followed by 9 bits for the third (blue) |
| mantissa. We don't want to promote different arrangements of |
| the bitfields for rgb9e5 values. |
| |
| 16) Can you use the GL_UNSIGNED_INT_5_9_9_9_REV_EXT format with |
| just any format? |
| |
| RESOLVED: You can only use the GL_UNSIGNED_INT_5_9_9_9_REV_EXT |
| format with GL_RGB. Otherwise, the GL generates |
| an GL_INVALID_OPERATION error. Conceptually, |
| GL_UNSIGNED_INT_5_9_9_9_REV_EXT is a 3-component format |
| that just happens to have 5 shared bits too. Just as the |
| GL_UNSIGNED_BYTE_3_3_2 format just works with GL_RGB (or else |
| the GL generates an GL_INVALID_OPERATION error), so should |
| GL_UNSIGNED_INT_5_9_9_9_REV_EXT. |
| |
| 17) What should GL_TEXTURE_SHARED_SIZE_EXT return when queried with |
| GetTexLevelParameter? |
| |
| RESOLVED: Return 5 for the RGB9_E5_EXT internal format and 0 |
| for all other existing formats. |
| |
| This is a count of the number of bits in the shared exponent. |
| |
| 18) What should GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, and |
| GL_TEXTURE_BLUE_SIZE return when queried with GetTexLevelParameter |
| for a GL_RGB9_E5_EXT texture? |
| |
| RESOLVED: Return 9 for each. |
| |
| Revision History |
| |
| Rev. Date Author Changes |
| ---- -------- -------- -------------------------------------------- |
| 0.5 02/18/07 mjk Initial public version |
| 1.0 07/18/08 mjk correct significant errors in spec language |
| and C code |