@@ -22,6 +22,10 @@ import { ByeGLFramebuffer } from './framebuffer.ts';
2222import { ByeGLProgram , ByeGLShader } from './program.ts' ;
2323import { Remapper } from './remap.ts' ;
2424import { ByeGLTexture } from './texture.ts' ;
25+ import {
26+ convertRGBToRGBA ,
27+ getTextureFormat ,
28+ } from './texture-format-mapping.ts' ;
2529import { $internal } from './types.ts' ;
2630import { ByeGLUniformLocation , UniformBufferCache } from './uniform.ts' ;
2731import type { UniformInfo , WgslGenerator } from './wgsl/wgsl-generator.ts' ;
@@ -105,6 +109,10 @@ export class ByeGLContext {
105109 [ gl . BLEND_DST_RGB , gl . ZERO ] ,
106110 [ gl . BLEND_SRC_ALPHA , gl . ONE ] ,
107111 [ gl . BLEND_DST_ALPHA , gl . ZERO ] ,
112+ [ gl . UNPACK_FLIP_Y_WEBGL , 0 ] ,
113+ [ gl . UNPACK_PREMULTIPLY_ALPHA_WEBGL , 0 ] ,
114+ [ gl . UNPACK_COLORSPACE_CONVERSION_WEBGL , gl . BROWSER_DEFAULT_WEBGL ] ,
115+ [ gl . UNPACK_ALIGNMENT , 4 ] ,
108116 ] ) ;
109117
110118 #globalAttributeState: AttributeState = {
@@ -277,7 +285,19 @@ export class ByeGLContext {
277285 if ( ! texture ) {
278286 textureMap . delete ( target ) ;
279287 } else {
280- textureMap . set ( target , texture ) ;
288+ if ( target === gl . TEXTURE_CUBE_MAP ) {
289+ textureMap . set ( gl . TEXTURE_CUBE_MAP_NEGATIVE_X , texture ) ;
290+ textureMap . set ( gl . TEXTURE_CUBE_MAP_POSITIVE_X , texture ) ;
291+ textureMap . set ( gl . TEXTURE_CUBE_MAP_NEGATIVE_Y , texture ) ;
292+ textureMap . set ( gl . TEXTURE_CUBE_MAP_POSITIVE_Y , texture ) ;
293+ textureMap . set ( gl . TEXTURE_CUBE_MAP_NEGATIVE_Z , texture ) ;
294+ textureMap . set ( gl . TEXTURE_CUBE_MAP_POSITIVE_Z , texture ) ;
295+ textureMap . set ( gl . TEXTURE_CUBE_MAP_NEGATIVE_Z , texture ) ;
296+ textureMap . set ( gl . TEXTURE_CUBE_MAP_POSITIVE_Z , texture ) ;
297+ textureMap . set ( target , texture ) ;
298+ } else {
299+ textureMap . set ( target , texture ) ;
300+ }
281301 }
282302 }
283303
@@ -1228,8 +1248,7 @@ export class ByeGLContext {
12281248 }
12291249
12301250 pixelStorei ( pname : GLenum , param : GLint ) : void {
1231- // TODO: Implement
1232- throw new NotImplementedYetError ( 'gl.pixelStorei' ) ;
1251+ this . #parameters. set ( pname , param ) ;
12331252 }
12341253
12351254 polygonOffset ( factor : GLfloat , units : GLfloat ) : void {
@@ -1331,54 +1350,80 @@ export class ByeGLContext {
13311350 let width = 0 ;
13321351 let height = 0 ;
13331352 let format : number = gl . RGBA ;
1334- // TODO: Not sure what to do with 'internalformat' just yet.
1353+ let type : number = gl . UNSIGNED_BYTE ;
13351354
13361355 const textureMap = this . #boundTexturesMap. get ( this . #activeTextureUnit) ;
1356+
13371357 const texture = textureMap ?. get ( target ) ?. [ $internal ] ;
13381358 if ( ! texture ) {
13391359 // TODO: Generate a WebGL appropriate error
13401360 return ;
13411361 }
13421362
13431363 if ( rest . length === 6 ) {
1344- const [ width_ , height_ , border , format_ , type , pixels ] = rest ;
1364+ const [ width_ , height_ , border , format_ , type_ , pixels ] = rest ;
13451365 width = width_ ;
13461366 height = height_ ;
13471367 format = format_ ;
1368+ type = type_ ;
1369+
1370+ const formatInfo = getTextureFormat ( format , type , internalformat ) ;
1371+ texture . setFormatInfo ( formatInfo ) ;
13481372
13491373 const size = [ width , height ] as const ;
13501374 texture . size = size ;
13511375
13521376 if ( pixels ) {
1353- // For now, assume RGBA/UNSIGNED_BYTE format
1354- // TODO: Handle different format/type combinations
1355- if ( format === gl . RGBA && type === gl . UNSIGNED_BYTE ) {
1356- this . #root. device . queue . writeTexture (
1357- { texture : texture . gpuTexture } ,
1358- pixels as ArrayBufferView < ArrayBuffer > ,
1359- { bytesPerRow : width * 4 , rowsPerImage : height } ,
1360- { width, height }
1361- ) ;
1362- } else {
1363- throw new NotImplementedYetError ( `gl.texImage2D with format=${ format } , type=${ type } ` ) ;
1377+ let dataToUpload = pixels as ArrayBufferView ;
1378+ let bytesPerRow = width * formatInfo . bytesPerPixel ;
1379+
1380+ if ( format === gl . RGB && ( type === gl . UNSIGNED_BYTE || type === gl . UNSIGNED_SHORT_5_6_5 ) ) {
1381+ if ( type === gl . UNSIGNED_BYTE ) {
1382+ const rgbaData = convertRGBToRGBA ( pixels , width , height ) ;
1383+ dataToUpload = rgbaData ;
1384+ bytesPerRow = width * 4 ;
1385+ }
13641386 }
1387+
1388+ const flipY = ! ! this . #parameters. get ( gl . UNPACK_FLIP_Y_WEBGL ) ;
1389+
1390+ if ( flipY && type === gl . UNSIGNED_BYTE ) {
1391+ dataToUpload = this . #flipImageVertically( dataToUpload as Uint8Array , width , height , formatInfo . bytesPerPixel ) ;
1392+ }
1393+
1394+ this . #root. device . queue . writeTexture (
1395+ { texture : texture . gpuTexture } ,
1396+ dataToUpload as ArrayBufferView < ArrayBuffer > ,
1397+ { bytesPerRow, rowsPerImage : height } ,
1398+ { width, height }
1399+ ) ;
13651400 }
13661401 } else {
1367- const [ _format , type , source ] = rest ;
1402+ const [ _format , _type , source ] = rest ;
13681403 format = _format ;
1404+ type = _type ;
1405+
1406+ const formatInfo = getTextureFormat ( format , type , internalformat ) ;
1407+ texture . setFormatInfo ( formatInfo ) ;
1408+
13691409 if ( 'width' in source ) {
13701410 width = source . width ;
13711411 height = source . height ;
13721412 } else {
13731413 width = source . displayWidth ;
13741414 height = source . displayHeight ;
13751415 }
1376- // TODO: Do something with 'type'
1416+
13771417 const size = [ width , height ] as const ;
13781418 texture . size = size ;
1379- this . #root. device . queue . copyExternalImageToTexture ( { source } , {
1380- texture : texture . gpuTexture ,
1381- } , size ) ;
1419+
1420+ const flipY = ! ! this . #parameters. get ( gl . UNPACK_FLIP_Y_WEBGL ) ;
1421+
1422+ this . #root. device . queue . copyExternalImageToTexture (
1423+ { source, flipY } ,
1424+ { texture : texture . gpuTexture } ,
1425+ size
1426+ ) ;
13821427 }
13831428
13841429 // TODO: Implement mip-mapping
@@ -1438,19 +1483,100 @@ export class ByeGLContext {
14381483 texture . setParameter ( pname , param ) ;
14391484 }
14401485
1441- texSubImage2D (
1486+ texStorage2D (
14421487 target : GLenum ,
1443- level : GLint ,
1444- xoffset : GLint ,
1445- yoffset : GLint ,
1488+ levels : GLsizei ,
1489+ internalformat : GLenum ,
14461490 width : GLsizei ,
14471491 height : GLsizei ,
1448- format : GLenum ,
1449- type : GLenum ,
1450- pixels : ArrayBufferView | null ,
14511492 ) : void {
1452- // TODO: Implement
1453- throw new NotImplementedYetError ( 'gl.texSubImage2D' ) ;
1493+ const textureMap = this . #boundTexturesMap. get ( this . #activeTextureUnit) ;
1494+ const texture = textureMap ?. get ( target ) ?. [ $internal ] ;
1495+ if ( ! texture ) {
1496+ return ;
1497+ }
1498+
1499+ const formatInfo = getTextureFormat (
1500+ gl . RGBA ,
1501+ gl . UNSIGNED_BYTE ,
1502+ internalformat ,
1503+ ) ;
1504+ texture . setFormatInfo ( formatInfo ) ;
1505+
1506+ texture . size = [ width , height ] ;
1507+ }
1508+
1509+ // biome-ignore format: Easier to read
1510+ texSubImage2D ( target : GLenum , level : GLint , xoffset : GLint , yoffset : GLint , width : GLsizei , height : GLsizei , format : GLenum , type : GLenum , pixels : ArrayBufferView | null ) : void ;
1511+ // biome-ignore format: Easier to read
1512+ texSubImage2D ( target : GLenum , level : GLint , xoffset : GLint , yoffset : GLint , format : GLenum , type : GLenum , source : TexImageSource ) : void ;
1513+ // biome-ignore format: Easier to read
1514+ texSubImage2D ( target : GLenum , level : GLint , xoffset : GLint , yoffset : GLint , ...rest : [ width : GLsizei , height : GLsizei , format : GLenum , type : GLenum , pixels : ArrayBufferView | null ] | [ format : GLenum , type : GLenum , source : TexImageSource ] ) : void {
1515+ const textureMap = this . #boundTexturesMap. get ( this . #activeTextureUnit) ;
1516+ const texture = textureMap ?. get ( target ) ?. [ $internal ] ;
1517+ if ( ! texture ) {
1518+ return ;
1519+ }
1520+
1521+ if ( rest . length === 5 ) {
1522+ // ArrayBufferView version
1523+ const [ width , height , format , type , pixels ] = rest ;
1524+
1525+ if ( ! pixels ) {
1526+ return ;
1527+ }
1528+
1529+ const flipY = ! ! this . #parameters. get ( gl . UNPACK_FLIP_Y_WEBGL ) ;
1530+ let dataToUpload = pixels as ArrayBufferView ;
1531+
1532+ if ( format === gl . RGB && type === gl . UNSIGNED_BYTE ) {
1533+ dataToUpload = convertRGBToRGBA ( pixels , width , height ) ;
1534+ }
1535+
1536+ if ( flipY && type === gl . UNSIGNED_BYTE ) {
1537+ const bytesPerPixel = format === gl . RGBA ? 4 : format === gl . RGB ? 3 : 1 ;
1538+ dataToUpload = this . #flipImageVertically( dataToUpload as Uint8Array , width , height , bytesPerPixel ) ;
1539+ }
1540+ if ( ( format === gl . RGBA || format === gl . RGB ) && type === gl . UNSIGNED_BYTE ) {
1541+ this . #root. device . queue . writeTexture (
1542+ {
1543+ texture : texture . gpuTexture ,
1544+ origin : { x : xoffset , y : yoffset , z : 0 } ,
1545+ mipLevel : level ,
1546+ } ,
1547+ dataToUpload as ArrayBufferView < ArrayBuffer > ,
1548+ {
1549+ bytesPerRow : width * 4 ,
1550+ rowsPerImage : height ,
1551+ } ,
1552+ { width, height, depthOrArrayLayers : 1 }
1553+ ) ;
1554+ } else {
1555+ throw new NotImplementedYetError ( `gl.texSubImage2D with format=${ format } , type=${ type } ` ) ;
1556+ }
1557+ } else {
1558+ const [ format , type , source ] = rest ;
1559+ const flipY = ! ! this . #parameters. get ( gl . UNPACK_FLIP_Y_WEBGL ) ;
1560+
1561+ let width : number , height : number ;
1562+ if ( 'width' in source ) {
1563+ width = source . width ;
1564+ height = source . height ;
1565+ } else {
1566+ width = source . displayWidth ;
1567+ height = source . displayHeight ;
1568+ }
1569+
1570+ this . #root. device . queue . copyExternalImageToTexture (
1571+ { source, flipY } ,
1572+ {
1573+ texture : texture . gpuTexture ,
1574+ origin : { x : xoffset , y : yoffset , z : 0 } ,
1575+ mipLevel : level ,
1576+ } ,
1577+ [ width , height ]
1578+ ) ;
1579+ }
14541580 }
14551581
14561582 uniform1f ( location : ByeGLUniformLocation | null , value : GLfloat ) {
@@ -1601,6 +1727,28 @@ export class ByeGLContext {
16011727 }
16021728 }
16031729
1730+ #flipImageVertically(
1731+ data : Uint8Array ,
1732+ width : number ,
1733+ height : number ,
1734+ bytesPerPixel : number ,
1735+ ) : Uint8Array {
1736+ const rowBytes = width * bytesPerPixel ;
1737+ const flipped = new Uint8Array ( data . length ) ;
1738+
1739+ for ( let row = 0 ; row < height ; row ++ ) {
1740+ const sourceRow = height - 1 - row ;
1741+ const sourceStart = sourceRow * rowBytes ;
1742+ const destStart = row * rowBytes ;
1743+
1744+ for ( let col = 0 ; col < rowBytes ; col ++ ) {
1745+ flipped [ destStart + col ] = data [ sourceStart + col ] ;
1746+ }
1747+ }
1748+
1749+ return flipped ;
1750+ }
1751+
16041752 uniformMatrix2fv (
16051753 location : ByeGLUniformLocation | null ,
16061754 transpose : GLboolean ,
@@ -1727,8 +1875,21 @@ export class ByeGLContext {
17271875 gl . TEXTURE0 + ( this . #uniformBufferCache. getValue ( uniform . id ) as number ) ;
17281876
17291877 const textureMap = this . #boundTexturesMap. get ( textureUnit ) ;
1730- // TODO: Always getting the TEXTURE_2D binding, but make it depend on the texture type
1731- const texture = textureMap ?. get ( gl . TEXTURE_2D ) ;
1878+ const typeToTextureBinding = {
1879+ 'texture_2d<f32>' : gl . TEXTURE_2D ,
1880+ 'texture_2d_array<f32>' : gl . TEXTURE_2D_ARRAY ,
1881+ 'texture_cube<f32>' : gl . TEXTURE_CUBE_MAP ,
1882+ 'texture_3d<f32>' : gl . TEXTURE_3D ,
1883+ 'texture_2d<u32>' : gl . TEXTURE_2D ,
1884+ } ;
1885+ const textureBinding =
1886+ typeToTextureBinding [
1887+ uniform . type . type as keyof typeof typeToTextureBinding
1888+ ] ;
1889+ if ( ! textureBinding ) {
1890+ throw new Error ( `Unsupported texture type: ${ uniform . type . type } ` ) ;
1891+ }
1892+ const texture = textureMap ?. get ( textureBinding ) ;
17321893
17331894 if ( ! texture ) {
17341895 throw new Error ( `Texture not found for unit ${ textureUnit } ` ) ;
0 commit comments