skia / external / github.com / KhronosGroup / OpenGL-Registry / 5f3fea090d8df5638b6df385c8b431f51b284651 / . / extensions / EXT / EXT_texture_shared_exponent.txt

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 | |

a 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 a 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 |