1Name 2 3 NV_explicit_multisample 4 5Name Strings 6 7 GL_NV_explicit_multisample 8 9Contributors 10 11 Eric Werness, NVIDIA Corporation 12 Pat Brown, NVIDIA Corporation 13 Jamie Gennis, NVIDIA Corporation 14 Nacho Sanz-Pastor, Aechelon 15 Mark Kilgard, NVIDIA Corporation 16 17Contact 18 19 Eric Werness, NVIDIA Corporation (ewerness 'at' nvidia.com) 20 Pat Brown, NVIDIA Corporation (pbrown 'at' nvidia.com) 21 22Status 23 24 Shipping in NVIDIA's Release 180 drivers (October 2008) 25 26Version 27 28 Last Modified Date: 10/27/08 29 Revision: 1 30 31Number 32 33 357 34 35Dependencies 36 37 OpenGL 2.0 is required. 38 39 ARB_multisample is required. 40 41 EXT_framebuffer_multisample affects the definition of this extension. 42 43 NV_gpu_program4 affects the definition of this extension. 44 45 EXT_gpu_shader4 affects the definition of this extension. 46 47 EXT_timer_query affects the definition of this extension. 48 49 This extension is written against the OpenGL 2.0 specification. 50 51Overview 52 53 In traditional multisample specs, the API only allows access to the samples 54 indirectly through methods such as coverage values and downsampled 55 readbacks. NV_explicit_multisample adds a set of new capabilities to allow 56 more precise control over the use of multisamples. Specifically, it adds: 57 58 * A query in the API to query the location of samples within the pixel 59 60 * An explicit control for the multisample sample mask to augment the 61 control provided by SampleCoverage 62 63 * A new texture target to wrap a renderbuffer and allow a restricted class 64 of accesses to the samples 65 66 * The ability to fetch a specific sample from a multisampled texture from 67 within a shader 68 69 * A program option to enable the new behavior 70 71New Procedures and Functions 72 73 void GetBooleanIndexedvEXT(enum value, uint index, boolean *data); 74 75 void GetIntegerIndexedvEXT(enum value, uint index, int *data); 76 77 void GetMultisamplefvNV(enum pname, uint index, float *val); 78 79 void SampleMaskIndexedNV(uint index, bitfield mask); 80 81 void TexRenderbufferNV(enum target, uint renderbuffer); 82 83New Tokens 84 85 Accepted by the <pname> parameter of GetMultisamplefvNV: 86 87 SAMPLE_POSITION_NV 0x8E50 88 89 Accepted by the <cap> parameter of Enable, Disable, and IsEnabled, and by 90 the <pname> parameter of GetBooleanv, GetIntegerv, GetFloatv, and 91 GetDoublev: 92 93 SAMPLE_MASK_NV 0x8E51 94 95 Accepted by the <pname> parameter of GetBooleanIndexedvEXT and 96 GetIntegerIndexedvEXT: 97 98 SAMPLE_MASK_VALUE_NV 0x8E52 99 100 Accepted by the <pname> parameter of GetBooleanv, GetDoublev, GetIntegerv, 101 and GetFloatv: 102 103 TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53 104 TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54 105 MAX_SAMPLE_MASK_WORDS_NV 0x8E59 106 107 Accepted by the <target> parameter of BindTexture, and TexRenderbufferNV: 108 109 TEXTURE_RENDERBUFFER_NV 0x8E55 110 111 Returned by the <type> parameter of GetActiveUniform: 112 113 SAMPLER_RENDERBUFFER_NV 0x8E56 114 INT_SAMPLER_RENDERBUFFER_NV 0x8E57 115 UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58 116 117Additions to Chapter 2 of the OpenGL 2.0 Specification (OpenGL Operation) 118 119 Modify Section 2.X.2, Program Grammar 120 121 If a program specifies the NV_explicit_multisample program option, the 122 <texTarget> rule is modified to add the texture target RENDERBUFFER and the 123 <TEXop> rule is modified to add the instruction TXFMS. 124 125 Add to section 2.15.4.1, Shader Only Texturing 126 127 Multisample Texel Fetches 128 129 Multisample texel fetches are very similar to standard texel fetches. Since 130 multisample buffers can't have mipmaps, there is no LOD parameter. Instead, 131 there is an integer parameter which selects the sample number to be fetched 132 from the buffer. The number identifying the sample is the same as the value 133 used to query the sample location using GetMultisamplefvNV. 134 135 Additionally, this fetch may only be performed on a texture renderbuffer 136 sampler. No other sample or fetch commands may be performed on a texture 137 renderbuffer sampler. 138 139 Add to section 2.X.4.4, Program Texture Access 140 141 Add row to table X.17 142 143 coordinates used 144 texTarget Texture Type s t r layer shadow 145 ---------------- --------------------- ----- ----- ------ 146 RENDERBUFFER TEXTURE_RENDERBUFFER_NV <not supported> 147 148 149 Add row to table X.18 150 151 coordinates used 152 texTarget supported i j k layer lod 153 ---------------- --------- ----- ----- --- 154 RENDERBUFFER no - - - - - 155 156 Add after the discussion of TexelFetch 157 158 The TXFMS instruction provides the ability to extract a single sample from a 159 specified RENDERBUFFER texture image using the function 160 161 result_t_vec TexelFetchMultisample(int_vec coord, int_vec offset); 162 163 The extracted texel is converted to an (R,G,B,A) vector according to Table 164 3.21. The result vector is interpreted as floating-point, signed integer, 165 or unsigned integer, according to the data type modifier of the 166 instruction. If the internal format of the texture is not compatible with 167 the instruction's data type modifier, the extracted texel value is 168 undefined. 169 170 <coord> is a four-component signed integer vector used to identify the 171 single sample accessed. Since a renderbuffer must be 2D with no mipmaps, the 172 x and y components are always used to select the pixel and the fourth 173 component is used to select the sample. All of the restrictions of 174 TexelFetch apply to TexelFetchMultisample, with the additional requirement 175 that it must be called on a RENDERBUFFER texture target. If the sample number 176 is greater than or equal to the value of SAMPLES for the render buffer 177 associated with the renderbuffer texture, the results are undefined. 178 179Additions to Chapter 3 of the OpenGL 2.0 Specification (Rasterization) 180 181 (Insert into section 3.2.1, Multisampling after the discussion of the query 182 for SAMPLES) 183 184 To query the location of a given sample, the function 185 186 void GetMultisamplefvNV(enum pname, uint index, float *val); 187 188 is used with a pname of SAMPLE_POSITION_NV and an index corresponding to the 189 sample for which the location should be returned. The sample location is 190 returned as two floating point values each between 0 and 1 corresponding to 191 the x and y locations respectively in GL pixel space of that sample. The 192 error INVALID_OPERATION is generated if <index> is greater than or equal to 193 the value of SAMPLES. 194 195 (Insert new Section 3.8.4, Renderbuffer Textures. Renumber subsequent 196 sections.) 197 198 In addition to one-, two-, and three-dimensional and cube map textures 199 described in previous sections, one additional type of texture is supported. 200 A renderbuffer texture is similar to a two-dimensional texture. However, 201 unlike other texture types, the texel array is not stored as part of the 202 texture. Instead, a renderbuffer object is attached to a renderbuffer 203 texture and the texel array is taken from the data store of the attached 204 renderbuffer object. When the contents of a renderbuffer are modified, 205 those changes are reflected in the contents of any renderbuffer texture to 206 which the buffer object is attached. Also unlike other textures, 207 renderbuffer textures do not have multiple image levels; only a single data 208 store is available. 209 210 The command 211 212 void TexRenderbufferNV(enum target, uint renderbuffer); 213 214 attaches the renderbuffer object named <renderbuffer> to the active 215 renderbuffer texture. If <renderbuffer> is zero, any renderbuffer object 216 attached to the buffer texture is detached, and no new renderbuffer object 217 is attached. If <renderbuffer> is non-zero, but is not the name of an 218 existing renderbuffer object, the error INVALID_OPERATION is generated. 219 <target> must be TEXTURE_RENDERBUFFER_NV. The name of the renderbuffer 220 object that is bound as the data store of the active renderbuffer texture 221 can be queried by calling GetIntegerv with <value> set to 222 TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV. 223 224 When a renderbuffer texture is accessed in a shader, the access takes one 225 vector of integers describing which pixel to fetch and an integer 226 corresponding to the sample numbers described in section 3.2.1 describing 227 which sample within the pixel to fetch. No standard sampling instructions 228 are allowed to the renderbuffer texture target. 229 230 Modify Section 3.8.10, Texture Completeness (p. 177) 231 232 (insert after second paragraph of section, p.177) 233 234 For renderbuffer textures, a texture is <complete> if a renderbuffer object 235 is attached to it, and each dimension of that renderbuffer is positive. 236 237 Modify Section 3.8.11, Texture State and Proxy State (p. 178) 238 239 (insert into the first paragraph of the section, p. 178) ... a zero 240 compressed size, and zero-sized components). The renderbuffer texture 241 target contains an integer identifying the renderbuffer object that 242 provides the data store for the texture, initially zero. Next, there are 243 the two sets of texture properties; ... 244 245 Modify Section 3.8.12, Texture Objects (p. 180) 246 247 (modify first paragraphs of section, p. 180, simply adding references to 248 renderbuffer textures, which are treated as texture objects) 249 250 In addition to the default textures TEXTURE_1D, TEXTURE_2D, TEXTURE_3D, 251 TEXTURE_CUBE_MAP, and TEXTURE_RENDERBUFFER_NV, named one-, two-, and 252 three-dimensional, cube map, and renderbuffer texture objects can be created 253 and operated upon. The name space for texture objects is the unsigned 254 integers, with zero reserved by the GL. 255 256 A texture object is created by binding an unused name to TEXTURE_1D, 257 TEXTURE_2D, TEXTURE_3D, TEXTURE_CUBE_MAP, or TEXTURE_RENDERBUFFER_NV. The 258 binding is effected by calling 259 260 void BindTexture( enum target, uint texture ); 261 262 with target set to the desired texture target and texture set to the unused 263 name. The resulting texture object is a new state vector, comprising all 264 the state values listed in section 3.8.11, set to the same initial 265 values. If the new texture object is bound to TEXTURE_1D, TEXTURE_2D, 266 TEXTURE_3D, TEXTURE_CUBE_MAP, or TEXTURE_RENDERBUFFER_NV, it is and remains 267 a one-, two-, three-dimensional, cube map, or renderbuffer texture 268 respectively until it is deleted. 269 270 BindTexture may also be used to bind an existing texture object to either 271 TEXTURE_1D, TEXTURE_2D, TEXTURE_3D, TEXTURE_CUBE_MAP, or 272 TEXTURE_RENDERBUFFER_NV. The error INVALID_OPERATION is generated if an 273 attempt is made to bind a texture object of different dimensionality than 274 the specified target. If the bind is successful no change is made to the 275 state of the bound texture object, and any previous binding to target is 276 broken. 277 278 ... 279 280 In the initial state, TEXTURE_1D, TEXTURE_2D, TEXTURE_3D, TEXTURE_CUBE_MAP, 281 and TEXTURE_RENDERBUFFER_NV have one-, two-, three-dimensional, cube map, 282 and renderbuffer texture state vectors respectively associated with them. In 283 order that access to these initial textures not be lost, they are treated as 284 texture objects all of whose names are 0. The initial one-, two-, 285 three-dimensional, cube map, and renderbuffer texture is therefore operated 286 upon, queried, and applied as TEXTURE_1D, TEXTURE_2D, TEXTURE_3D, 287 TEXTURE_CUBE_MAP, or TEXTURE_RENDERBUFFER_NV respectively while 0 is bound 288 to the corresponding targets. 289 290 Texture objects are deleted by calling 291 292 void DeleteTextures( sizei n, uint *textures ); 293 294 textures contains n names of texture objects to be deleted. After a texture 295 object is deleted, it has no contents or dimensionality, and its name is 296 again unused. If a texture that is currently bound to one of the targets 297 TEXTURE_1D, TEXTURE_2D, TEXTURE_3D, TEXTURE_CUBE_MAP, or 298 TEXTURE_RENDERBUFFER_NV is deleted, it is as though BindTexture had been 299 executed with the same target and texture zero. Unused names in textures are 300 silently ignored, as is the value zero. 301 302 (modify second paragraph, p. 182, adding buffer textures, plus cube map 303 textures, which is an oversight in the core specification) 304 305 The texture object name space, including the initial one-, two-, and 306 three-dimensional, cube map, and renderbuffer texture objects, is shared 307 among all texture units. A texture object may be bound to more than one 308 texture unit simultaneously. After a texture object is bound, any GL 309 operations on that target object affect any other texture units to which the 310 same texture object is bound. 311 312Additions to Chapter 4 of the OpenGL 2.0 Specification (Per-Fragment Operations 313and the Frame Buffer) 314 315 Modify Section 4.1.3, Multisample Fragment Operations 316 317 Modify the first paragraph to include SAMPLE_MASK and SAMPLE_MASK_VALUE on 318 the list of values the coverage value is modified based on. 319 320 Modify the discussion of SAMPLE_COVERAGE to start with "Next" instead of 321 "Finally" 322 323 Add after the discussion of SAMPLE_COVERAGE: 324 325 Finally, if SAMPLE_MASK is enabled, the fragment coverage is ANDed with the 326 coverage value SAMPLE_MASK_VALUE. The value of SAMPLE_MASK_VALUE is 327 specified using 328 329 void SampleMaskIndexedNV(GLuint index, GLbitfield mask); 330 331 with <mask> set to the desired mask for <maskNumber>. SAMPLE_MASK_VALUE is 332 queried by calling GetIntegerIndexedv with <pname> set to SAMPLE_MASK_VALUE 333 and the index set to <maskNumber>. Bit B of mask M corresponds to sample 334 32*M+B as described in Section 3.2.1. The error INVALID_OPERATION is 335 generated if the mask word indexed is greater than or equal to 336 MAX_SAMPLE_MASK_WORDS. 337 338 339Additions to Chapter 5 of the OpenGL 2.0 Specification (Special Functions) 340 341 On p. 241, add the following to the list of commands not compiled into a 342 display list: GetMultisamplefvNV, TexRenderbufferNV. 343 344Additions to Chapter 6 of the OpenGL 2.0 Specification (State and 345State Requests) 346 347 Modify the second paragraph of section 6.1.1 (Simple Queries) 348 p244 to read as follows: 349 350 ...<data> is a pointer to a scalar or array of the indicated 351 type in which to place the returned data. 352 353 void GetBooleanIndexedvEXT(enum target, uint index, boolean *data); 354 void GetIntegerIndexedvEXT(enum target, uint index, int *data); 355 356 are used to query indexed state. <target> is the name of 357 the indexed state and <index> is the index of the particular 358 element being queried. <data> is a pointer to a scalar or array 359 of the indicated type in which to place the returned data. In addition ... 360 361Additions to the AGL/GLX/WGL Specifications 362 363 None 364 365GLX Protocol 366 367 The following rendering command is sent to the server as part of 368 a glXRender request: 369 370 SampleMaskIndexedNV 371 372 2 12 rendering command length 373 2 XXXX rendering command opcode 374 4 CARD32 index 375 4 BITFIELD mask 376 377 TexRenderbufferNV 378 379 2 12 rendering command length 380 2 XXXX rendering command opcode 381 4 ENUM target 382 4 CARD32 renderbuffer 383 384 385 The following new non-rendering commands are added: 386 387 GetMultisamplefvNV 388 389 1 CARD8 opcode(X assigned) 390 1 XXX GLX opcode 391 2 4 request length 392 4 GLX_CONTEXT_TAG context tag 393 4 ENUM pname 394 4 CARD32 index 395 => 396 1 1 reply 397 1 unused 398 2 CARD16 sequence number 399 4 0 reply length 400 8 unused 401 4 FLOAT32 val[0] 402 4 FLOAT32 val[1] 403 8 unused 404 405 406 407Dependencies on EXT_framebuffer_multisample 408 409 If EXT_framebuffer_multisample is not available, all discussion involving 410 textures and renderbuffers is deleted. This only leaves the ability to query 411 the sample parameters of the current framebuffer. 412 413Dependencies on EXT_timer_query 414 415 If EXT_timer_query is not available, include its discussion of int64EXT and 416 uint64EXT. 417 418Dependencies on NV_gpu_program4 and EXT_gpu_shader4 419 420 If NV_gpu_program4 and EXT_gpu_shader4 are not supported, and no other 421 mechanism is provided to perform texture lookups into renderbuffer textures, 422 this extension is pointless, given that it provides no fixed-function 423 mechanism to access renderbuffer textures. 424 425 If EXT_gpu_shader4 is supported, the language below describes the 426 modifications to the shading language to support renderbuffer textures. If 427 not, the language below and in Chapter 2 should be removed. 428 429Errors 430 431 The error INVALID_VALUE is generated by GetBooleanIndexedvEXT and 432 GetIntegerIndexedvEXT if target is SAMPLE_MASK_VALUE_NV and index is greater 433 than or equal to MAX_SAMPLE_MASK_WORDS_NV. 434 435 The error INVALID_VALUE is generated by SampleMaskIndexedNV if index is greater 436 than or equal to MAX_SAMPLE_MASK_WORDS_NV. 437 438 439New State 440 441 Get Value Get Command Type Initial Value Attribute 442 --------- ----------- ---- ------------- --------- 443 SAMPLE_MASK_NV IsEnabled B FALSE multisample/enable 444 SAMPLE_MASK_VALUE_NV GetInteger- nxZ+ ~0 multisample 445 IndexedvEXT 446 447 Where n is the number of sample mask words (the value of 448 MAX_SAMPLE_MASK_WORDS_NV) the implementation supports. 449 450 (add to table 6.15, Texture State Per Texture Unit/Binding Point p. 276) 451 452 Initial 453 Get Value Type Get Command Value Description Sec. Attribute 454 --------------------------------- ---- ----------- ------- --------------------------- ------ --------- 455 TEXTURE_BINDING_RENDERBUFFER_NV 2*xZ+ GetIntegerv 0 Texture object bound to 3.8.12 texture 456 TEXTURE_RENDERBUFFER_EXT 457 458 (add to table 6.16, Texture State Per Texture Object, p. 276) 459 460 Initial 461 Get Value Type Get Command Value Description Sec. Attribute 462 --------------------------------- ---- ----------- ------- --------------------------- ------ --------- 463 TEXTURE_RENDERBUFFER_DATA_STORE_ nxZ+ GetIntegerv 0 Renderbuffer object bound 3.8.4 texture 464 BINDING_NV as the data store for the 465 active image unit's 466 renderbuffer texture 467 468 (add to table 9.nnn "Framebuffer dependent Values" introduced by 469 the EXT_framebuffer_object specification) 470 471 Initial 472 Get Value Type Get Command Value Description Sec. Attribute 473 ------------------ ---------- ------------------ -------------- --------------- ------ --------- 474 SAMPLE_POSITION_NV N*2*R[0,1] GetMultisamplefvNV implementation Explicit sample 3.2.1 - 475 dependent positions 476 477 Where N is the number of samples (the value of SAMPLES) the framebuffer supports. 478 479New Implementation Dependent State 480 481 Minimum 482 Get Value Type Get Command Value Description Sec. Attribute 483 --------- ------- ----------- ------- ---------------- ------ --------- 484 MAX_SAMPLE_MASK_WORDS_NV Z GetIntegerv 1 maximum number x.x.x - 485 of sample mask 486 words 487 488Modifications to the OpenGL Shading Language Specification, Version 1.10.59 489 490 Including the following line in a shader can be used to control the language 491 featured described in this extension: 492 493 #extension GL_NV_explicit_multisample : <behavior> 494 495 where <behavior> is as specified in section 3.3. 496 497 A new preprocessor #define is added to the OpenGL Shading Language: 498 499 #define GL_NV_explicit_multisample 1 500 501 Add to section 3.6 "Keywords" 502 503 The following new sampler types are added: 504 505 samplerRenderbuffer, isamplerRenderbuffer, usamplerRenderbuffer 506 507 Add to section 4.1 "Basic Types" 508 509 Add the following sampler type to the "default sampler types" table: 510 511 samplerRenderbuffer handle for accessing a renderbuffer texture 512 513 Add the following sampler type to the "unsigned integer sampler types" 514 table: 515 516 isamplerRenderbuffer handle for accessing an integer renderbuffer texture 517 518 Add the following sampler type to the "integer sampler types" table: 519 520 usamplerRenderbuffer handle for accessing an unsigned integer renderbuffer texture 521 522 Add to section 8.7 "Texture Lookup Functions" 523 524 Add new functions to the set of allowed texture lookup functions: 525 526 Syntax: 527 528 vec4 texelFetchRenderbuffer(samplerRenderbuffer sampler, ivec2 coord, int sample) 529 530 Description: 531 532 Use integer texture coordinate <coord> to lookup a single sample <sample> 533 on the texture bound to <sampler> as described in section 2.15.4.1 of the 534 OpenGL specification "Multisample Texel Fetches". 535 536 Syntax: 537 538 ivec2 textureSizeRenderbuffer(samplerRenderbuffer sampler) 539 ivec2 textureSizeRenderbuffer(isamplerRenderbuffer sampler) 540 ivec2 textureSizeRenderbuffer(usamplerRenderbuffer sampler) 541 542 Description: 543 544 Returns the dimensions, width and height of level 0 for the texture bound 545 to <sampler>, as described in section 2.15.4.1 of the OpenGL specification 546 section "Texture Size Query". 547 548Examples 549 550Issues 551 552 (1) Should the NUMBER_OF_SAMPLES parameter reuse an existing enum? 553 554 RESOLVED. Just use SAMPLES_ARB. 555 556 (2) How should we deal with sample patterns of more than 557 sizeof(GLbitfield)*8 bits for the sample mask? 558 559 RESOLVED. The API uses indexed calls to allow indefinite extension of the 560 number of samples exposed. 561 562 (3) What's the interaction between sample mask, sample coverage, and alpha 563 to coverage? 564 565 UNRESOLVED. In the hardware there's just one method for doing overall 566 coverage, so we can combine the two API states into the methods as we see 567 fit. The more interesting question is the interaction between the shader 568 sample mask from GPU_program4_1 and the API-level masks, which we should 569 try to keep similar to the API-level interface. 570 571 (4) Should we expose the CSAA weights in this API? 572 573 RESOLVED. Not in this extension. For now, only maskable samples can be 574 queried. A future extension can allow this functionality. 575 576 (5) What should the default value of SAMPLE_MASK be? 0? ~0? 577 578 RESOLVED. ~0 to match with other state like stencil mask. 579 580 (6) Should the sample position query work for non-multisample? 581 582 RESOLVED. The value of SAMPLES for non-multisample is zero, so the sample 583 position query will return an error for all index values in this case. 584 585 (7) Integer/unsigned integer renderbuffer sampler types? 586 587 RESOLVED. There are integer/unsigned integer renderbuffer sampler types. 588 589 (8) Do we need a program option to enable the new behavior? 590 591 RESOLVED. Yes. 592 593 (9) What should TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV be if the 594 renderbuffer object it refers to has been deleted? 595 596 RESOLVED. TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV remains unchanged 597 when the renderbuffer is deleted. 598 599 (10) Do renderbuffer textures support texture parameters 600 (TexParameter) or queries (GetTexParameter, 601 GetTexLevelParameter, GetTexImage)? 602 603 RESOLVED: No. None of the existing parameters apply to renderbuffer 604 textures, and this extension doesn't introduce the need for any new ones. 605 Renderbuffer textures have no levels, and the size in texels is implicit 606 (based on the data store). Given that the texels themselves are obtained 607 from a renderbuffer object, it seems more appropriate to retrieve such 608 data with renderbuffer queries. 609 610 Note that the spec edits above don't add explicit error language for any 611 of these cases. That is because each of the functions enumerate the set 612 of valid <target> parameters. Not editing the spec to allow 613 TEXTURE_RENDERBUFFER_NV in these cases means that target is not legal, and 614 an INVALID_ENUM error should be generated. 615 616 (11) What is the behavior of TexelFetchMultisample when given an 617 out-of-bounds value? 618 619 RESOLVED: Undefined. This spec explicitly lists the behavior as undefined 620 when given a sample number greater than SAMPLES-1 and inherits the 621 undefined behavior specified for TexelFetch when the normal 622 two-dimensional coordinate is outside the range of the texture. 623 624 625Revision History 626 627 Rev. Date Author Changes 628 ---- -------- -------- ----------------------------------------- 629 630 1 7/22/08 ewerness First revision. 631