Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGLRenderer WebGL2.0 initial support with GLSL 1 to 3 runtime conversion #13717

Merged
merged 41 commits into from
Jul 12, 2018
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
d1d38c9
WebGLRenderer WebGL2.0 basic support
takahirox Mar 28, 2018
63839ee
Minor clean up
takahirox Mar 28, 2018
b17356f
Add internalFormat conversion function to WebGLTextures
takahirox Mar 28, 2018
24e0ec4
Update setupFrameBufferTexture()
takahirox Mar 28, 2018
b3af65a
Skip GLSL conversion if material is RawShaderMaterial
takahirox Mar 28, 2018
b1f0db4
Ignore EXT_frag_depth for WebGL 2.0
takahirox Mar 28, 2018
a04bea6
Ignore EXT_shader_texture_lod for WebGL 2.0
takahirox Mar 28, 2018
5392545
Update instancing for WebGL 2.0
takahirox Mar 28, 2018
2b0dac6
Clean up WebGLUtils
takahirox Mar 28, 2018
cf3ff01
Clean up WebGLProgram
takahirox Mar 28, 2018
32cb672
Add .isGLSL3 to ShaderMaterial
takahirox Mar 28, 2018
2456569
Clean up WebGLTextures
takahirox Mar 28, 2018
6065f4f
Update EXT_blend_minmax handling for WebGL 2.0
takahirox Mar 28, 2018
f02117f
Update WebGLUtils for WebGL 2.0 UNSIGNED_INT_24_8
takahirox Mar 28, 2018
c1ff8fc
Update internalFormat for WebGL2.0
takahirox Mar 28, 2018
7a4dc58
Add WebGL_draw_buffers as built-in extensionn for WebGL2.0 to WebGLEx…
takahirox Mar 28, 2018
fd7ced8
Clean up WebGLProgram
takahirox Mar 28, 2018
949623b
Add webgl2 option to WebGLRenderer
takahirox Mar 28, 2018
89c1738
Update WebGLTextures for gl.RGB
takahirox Mar 29, 2018
42831e8
Update getInternalFormat() in WebGLTextures
takahirox Mar 29, 2018
51dad3f
Fix HalfFloatType bug in WebGLUtils
takahirox Mar 29, 2018
e11ed2b
Clean up WebGLUtils
takahirox Mar 29, 2018
ddc4893
Enable webgl2_sandbox example
takahirox Mar 30, 2018
4ec384f
Remove trailing space
takahirox Mar 30, 2018
b53cec2
Do not force POT texture for WebGL2
takahirox Mar 30, 2018
5219c0f
Save isWebGL2 to WebGL context object
takahirox Mar 31, 2018
3f0dfd9
Add webgl2 to Detector
takahirox Mar 31, 2018
062d7db
Remove .isGLSL3 property from ShaderMaterial and determine GLSL3 shad…
takahirox Apr 1, 2018
8ca782f
Rename webgl2 with autoWebgl2
takahirox Apr 4, 2018
325d8e9
Add webgl2 option to WebGLRenderer
takahirox Apr 4, 2018
3cdf35b
Combine webgl2 and autoWebgl2 options to webglVersion
takahirox Apr 4, 2018
dc11ded
Revert the change of WebGLRenderer.setRenderTarget
takahirox Apr 4, 2018
bdbc68b
Revert the change of setupFrameBufferTexture in WebGLTextures
takahirox Apr 4, 2018
46ee5b6
Remove workaround from WebGLExtensions and add if ( gl.isWebGL2 ) ins…
takahirox Apr 4, 2018
ab65cb7
Merge remote-tracking branch 'upstream/dev' into WebGLRendererWebGL2.0
takahirox May 2, 2018
acee27d
Remove webglVersion option from WebGLRenderer constructor
takahirox May 8, 2018
f696296
Revert "Remove webglVersion option from WebGLRenderer constructor"
takahirox May 8, 2018
d177df5
Revert "Enable webgl2_sandbox example"
takahirox May 8, 2018
158fc6c
Update webgl2_sandbox example
takahirox May 8, 2018
fda6fac
Remove webglVersion option from WebGLRenderer constructor.
takahirox May 8, 2018
a18d1a2
Merge branch 'dev' into WebGLRendererWebGL2.0
takahirox May 10, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/materials/ShaderMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ function ShaderMaterial( parameters ) {
this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}';
this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}';

this.isGLSL3 = false;
Copy link
Collaborator Author

@takahirox takahirox Mar 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property is for users who wanna write GLSL 3 shader code with ShaderMaterial. If this property is set true, we don't add

out highp vec4 pc_fragColor;
#define gl_FragColor pc_fragColor

to final shader code in WebGLProgram.

Unless doing that, compile fails because of multiple out declaration (without specifying layout).

I'm thinking better solution....

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option is we let users write #version in user shader code for GLSL 3 like

#version 300 es

out highp vec4 glFragColor;
void main() {
    glFragColor = vec4( 1.0 );
}

and we determine GLSL 3 if the user shader code begins with "#version 300 es".


this.linewidth = 1;

this.wireframe = false;
Expand Down
15 changes: 10 additions & 5 deletions src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ function WebGLRenderer( parameters ) {
_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
_powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default';
_powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default',
_webgl2 = parameters.webgl2 !== undefined ? parameters.webgl2 : false;

var currentRenderList = null;
var currentRenderState = null;
Expand Down Expand Up @@ -190,11 +191,15 @@ function WebGLRenderer( parameters ) {
_canvas.addEventListener( 'webglcontextlost', onContextLost, false );
_canvas.addEventListener( 'webglcontextrestored', onContextRestore, false );

_gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes );
var webglVersion = _webgl2 && typeof WebGL2RenderingContext !== 'undefined' ? 'webgl2' : 'webgl';
Copy link
Collaborator Author

@takahirox takahirox Mar 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should try to create WebGLContext if we fail to create WebGL2Context? (I don't)

Copy link
Collaborator

@Mugen87 Mugen87 Mar 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can avoid a fallback if the user explicitly requests WebGL2 with a parameter. Compatibility can be verified on app level. Maybe we can enhance Detector in order to support the user with this task. Something like:

if ( Detector.webgl2 ) // use WebGL 2

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for Detector.webgl2

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. Thanks!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @Mugen87 I think that if we explicitly request WebGL2 context and it's not available, it shouldn't fallback to webgl1. Probably someone requesting webgl2 would like to use specific features that will fail on webgl1.
We could decide in the future if use by default webgl2 if available and we don't specify any value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added two options. webgl2: force to create WebGL2Context, autoWebgl2: create WebGL2Context if browser seems to support WebGL 2.0 otherwise create WebGLContext.

We may want autoWebgl2 as the default once Three.js + WebGL 2.0 becomes stable (See #13701 (comment))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just leave one option. Something like:

  • webgl2: false -> webgl1
  • webgl2: true -> webgl2 or throw error if not possible
  • webgl2: 'auto' ? -> try webgl2 or fallback to webgl1.
  • webgl2: undefined -> right now it should be use webgl1, once webgl2 will be stable we could do the same as auto.

Copy link
Collaborator Author

@takahirox takahirox Apr 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combining the two options into a single one is nice. But I don't really like mix type, boolean or strings.

So how about webglVersion option,

  • 'webgl': webgl1
  • 'webgl2': webgl2
  • 'auto': webgl2 if browser seems to support WebGL 2.0, otherwise webgl1, or try webgl2 or fallback to webgl1
  • undefined: right now acts as 'webgl', once Three.js + WebGL 2.0 becomes stable acts as 'auto'

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@takahirox yeah! I like that approach better 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.


_gl = _context || _canvas.getContext( webglVersion, contextAttributes );

if ( _gl === null && webglVersion === 'webgl' ) _gl = _canvas.getContext( 'experimental-webgl', contextAttributes );

if ( _gl === null ) {

if ( _canvas.getContext( 'webgl' ) !== null ) {
if ( _canvas.getContext( webglVersion ) !== null ) {

throw new Error( 'Error creating WebGL context with your selected attributes.' );

Expand Down Expand Up @@ -2427,7 +2432,7 @@ function WebGLRenderer( parameters ) {

};

this.setRenderTarget = function ( renderTarget ) {
this.setRenderTarget = function ( renderTarget, optType ) {

_currentRenderTarget = renderTarget;

Expand Down Expand Up @@ -2469,7 +2474,7 @@ function WebGLRenderer( parameters ) {

if ( _currentFramebuffer !== framebuffer ) {

_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
_gl.bindFramebuffer( optType || _gl.FRAMEBUFFER, framebuffer );
Copy link
Collaborator Author

@takahirox takahirox Mar 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking to move this .setRenderTarget() update to another PR because this's for WebGL2.0 feature specific.

The purpose of this PR is to enable existing core to run on WebGL 2.0 rather than to add WebGL2.0 features.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep I agree with that approach

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted. This change should be discussed well in another PR.

_currentFramebuffer = framebuffer;

}
Expand Down
6 changes: 4 additions & 2 deletions src/renderers/webgl/WebGLBufferRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

function WebGLBufferRenderer( gl, extensions, info ) {

var isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There're duplicated this webgl2 context check line across modules.

Do you think it'd be worth to have isWebGL2 function in utils.js or somewhere?

function isWebGL2( gl ) {

    return typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext;

}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you determine that when the context is instantiated and save it as a property?

gl.isWebGL2 = boolean;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can if we don't mind polluting the context object.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, Thanks!


var mode;

function setMode( value ) {
Expand Down Expand Up @@ -37,11 +39,11 @@ function WebGLBufferRenderer( gl, extensions, info ) {

count = position.data.count;

extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount );
extension[ isWebGL2 ? 'drawArraysInstanced' : 'drawArraysInstancedANGLE' ]( mode, 0, count, geometry.maxInstancedCount );

} else {

extension.drawArraysInstancedANGLE( mode, start, count, geometry.maxInstancedCount );
extension[ isWebGL2 ? 'drawArraysInstanced' : 'drawArraysInstancedANGLE' ]( mode, start, count, geometry.maxInstancedCount );

}

Expand Down
35 changes: 33 additions & 2 deletions src/renderers/webgl/WebGLExtensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ function WebGLExtensions( gl ) {

var extensions = {};

var isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext );

return {

get: function ( name ) {
Expand All @@ -21,7 +23,17 @@ function WebGLExtensions( gl ) {
switch ( name ) {

case 'WEBGL_depth_texture':
extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );

if ( isWebGL2 ) {

extension = gl;

} else {

extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );

}

break;

case 'EXT_texture_filter_anisotropic':
Expand All @@ -41,7 +53,26 @@ function WebGLExtensions( gl ) {
break;

default:
extension = gl.getExtension( name );

if ( isWebGL2 &&
[ 'ANGLE_instanced_arrays',
'OES_texture_float',
'OES_texture_half_float',
'OES_texture_half_float_linear',
'OES_element_index_uint',
'OES_standard_derivatives',
'EXT_frag_depth',
'EXT_shader_texture_lod',
'EXT_blend_minmax',
'WEBGL_draw_buffers' ].indexOf( name ) >= 0 ) {

extension = gl;

} else {

extension = gl.getExtension( name );

}
Copy link
Collaborator Author

@takahirox takahirox Mar 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning gl for WebGL2.0 built-in features has Pros and Cons. Pros is less existing code change. Cons is a bit misleading.

For example, Three.js devs would think as if WebGL 2.0 also has ANGLE_instanced_arrays extension although it's built-in feature in WebGL 2.0 with this code.

var extension = extensions.get( 'ANGLE_instanced_arrays' );
extension[ isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, 0 );

Copy link
Collaborator Author

@takahirox takahirox Mar 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made branch, removing the workaround from WebGLExtensions and checking if context is webgl2 instead.

dev...takahirox:WebGLRendererWebGL2.0_2

The code will be

if ( isWebGL2 ) {

    gl.something();

} else {

    var extension = extensions.get( 'something' );
    if ( extension !== null ) extension.something_EXT();

}

Change will be a bit bigger, but less misleading. Personally I prefer this.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that returning gl on extensions feels strange. But I also don't like the second proposal that much, very verbose probably.
I know that I just wrote before that it's not a good idea to pollute the WebGLRenderContext, but the approach babylon is following is truly cleaner for the code: https://github.com/BabylonJS/Babylon.js/blob/ecbba4cd58b945732f10a474b897287cc322d14e/src/Engine/babylon.engine.ts#L1594
Maybe some mix approach where we save the reference to the extension or gl2 call to a variable on WebGLRenderer and use it directly? Not sure I'll think more about it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case I don't see it as a blocking issue, we could merge it and later decide the best way to fix this.

Copy link
Collaborator Author

@takahirox takahirox Apr 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with brushing up later. Personally I prefer starting with the second one because I like less misleading and probably we couldn't remove all if ( isWebGL2 ) {} else {} blocks even if WebGLExtensions returns gl.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated


}

Expand Down
4 changes: 3 additions & 1 deletion src/renderers/webgl/WebGLIndexedBufferRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

function WebGLIndexedBufferRenderer( gl, extensions, info ) {

var isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext;

var mode;

function setMode( value ) {
Expand Down Expand Up @@ -40,7 +42,7 @@ function WebGLIndexedBufferRenderer( gl, extensions, info ) {

}

extension.drawElementsInstancedANGLE( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount );
extension[ isWebGL2 ? 'drawElementsInstanced' : 'drawElementsInstancedANGLE' ]( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount );

info.update( count, mode, geometry.maxInstancedCount );

Expand Down
35 changes: 34 additions & 1 deletion src/renderers/webgl/WebGLProgram.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters

var gl = renderer.context;

var isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext;

var defines = material.defines;

var vertexShader = shader.vertexShader;
Expand Down Expand Up @@ -285,7 +287,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters

//

var customExtensions = generateExtensions( material.extensions, parameters, extensions );
var customExtensions = isWebGL2 ? '' : generateExtensions( material.extensions, parameters, extensions );

var customDefines = generateDefines( defines );

Expand Down Expand Up @@ -514,6 +516,37 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters
vertexShader = unrollLoops( vertexShader );
fragmentShader = unrollLoops( fragmentShader );

if ( isWebGL2 && ! material.isRawShaderMaterial ) {

var isGLSL3ShaderMaterial = material.isShaderMaterial && material.isGLSL3;

// GLSL 3.0 conversion
prefixVertex = [
'#version 300 es\n',
'#define attribute in',
'#define varying out',
'#define texture2D texture'
].join( '\n' ) + '\n' + prefixVertex;

prefixFragment = [
'#version 300 es\n',
'#define varying in',
isGLSL3ShaderMaterial ? '' : 'out highp vec4 pc_fragColor;',
isGLSL3ShaderMaterial ? '' : '#define gl_FragColor pc_fragColor',
'#define gl_FragDepthEXT gl_FragDepth',
'#define texture2D texture',
'#define textureCube texture',
'#define texture2DProj textureProj',
'#define texture2DLodEXT textureLod',
'#define texture2DProjLodEXT textureProjLod',
'#define textureCubeLodEXT textureLod',
'#define texture2DGradEXT textureGrad',
'#define texture2DProjGradEXT textureProjGrad',
'#define textureCubeGradEXT textureGrad'
].join( '\n' ) + '\n' + prefixFragment;

}

var vertexGlsl = prefixVertex + vertexShader;
var fragmentGlsl = prefixFragment + fragmentShader;

Expand Down
6 changes: 4 additions & 2 deletions src/renderers/webgl/WebGLState.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { Vector4 } from '../../math/Vector4.js';

function WebGLState( gl, extensions, utils ) {

var isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext;

function ColorBuffer() {

var locked = false;
Expand Down Expand Up @@ -430,7 +432,7 @@ function WebGLState( gl, extensions, utils ) {

var extension = extensions.get( 'ANGLE_instanced_arrays' );

extension.vertexAttribDivisorANGLE( attribute, 0 );
extension[ isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, 0 );
attributeDivisors[ attribute ] = 0;

}
Expand All @@ -452,7 +454,7 @@ function WebGLState( gl, extensions, utils ) {

var extension = extensions.get( 'ANGLE_instanced_arrays' );

extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );
extension[ isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute );
attributeDivisors[ attribute ] = meshPerAttribute;

}
Expand Down
Loading