blob: a8f902bb36dac3b8cae01048b5711dd4d5899d6f [file]
#include <GLES3/gl3.h>
#include <emscripten.h>
#include <emscripten/html5.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define WIDTH 4
#define HEIGHT 4
#define DEPTH 4
#define CHANNELS 4
static void check_pixel(const GLubyte *buf, int x, int y,
GLubyte r, GLubyte g, GLubyte b, GLubyte a,
const char *label) {
int i = (y * WIDTH + x) * CHANNELS;
if (buf[i] == r && buf[i+1] == g && buf[i+2] == b && buf[i+3] == a) {
printf("PASS: %s — got rgba(%d,%d,%d,%d)\n", label, r, g, b, a);
} else {
printf("FAIL: %s — expected rgba(%d,%d,%d,%d), got rgba(%d,%d,%d,%d)\n",
label, r, g, b, a, buf[i], buf[i+1], buf[i+2], buf[i+3]);
assert(0);
}
}
static void read_slice(GLuint fb, GLuint tex, int z, GLubyte *out) {
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, z);
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glReadPixels(0, 0, WIDTH, HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, out);
assert(glGetError() == GL_NO_ERROR);
}
int main() {
EmscriptenWebGLContextAttributes attrs;
emscripten_webgl_init_context_attributes(&attrs);
attrs.majorVersion = 2;
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context("#canvas", &attrs);
assert(ctx > 0);
assert(emscripten_webgl_make_context_current(ctx) == EMSCRIPTEN_RESULT_SUCCESS);
printf("PASS: WebGL2 context acquired\n");
/* ---- create 3D texture ---- */
GLuint tex;
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, tex);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexStorage3D(GL_TEXTURE_3D, 1, GL_RGBA8, WIDTH, HEIGHT, DEPTH);
assert(glGetError() == GL_NO_ERROR);
printf("PASS: texStorage3D allocated %dx%dx%d RGBA8\n", WIDTH, HEIGHT, DEPTH);
/* ---- fill and upload full volume ---- */
int sliceBytes = WIDTH * HEIGHT * CHANNELS;
int totalBytes = sliceBytes * DEPTH;
GLubyte *data = (GLubyte *)malloc(totalBytes);
assert(data);
for (int z = 0; z < DEPTH; z++)
for (int y = 0; y < HEIGHT; y++)
for (int x = 0; x < WIDTH; x++) {
int i = (z * HEIGHT * WIDTH + y * WIDTH + x) * CHANNELS;
data[i + 0] = (z * 64) & 0xff;
data[i + 1] = (y * 64) & 0xff;
data[i + 2] = (x * 64) & 0xff;
data[i + 3] = 255;
}
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0,
WIDTH, HEIGHT, DEPTH,
GL_RGBA, GL_UNSIGNED_BYTE, data);
assert(glGetError() == GL_NO_ERROR);
printf("PASS: texSubImage3D uploaded full volume (%d bytes)\n", totalBytes);
/* ---- overwrite z-slice 1 with solid red ---- */
GLubyte *sliceData = (GLubyte *)malloc(sliceBytes);
assert(sliceData);
for (int i = 0; i < sliceBytes; i += CHANNELS) {
sliceData[i] = 255; sliceData[i+1] = 0; sliceData[i+2] = 0; sliceData[i+3] = 255;
}
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 1,
WIDTH, HEIGHT, 1,
GL_RGBA, GL_UNSIGNED_BYTE, sliceData);
assert(glGetError() == GL_NO_ERROR);
printf("PASS: texSubImage3D overwrote z-slice 1 with solid red\n");
/* ---- patch 2x2 green rect at (1,1) in z-slice 2 ---- */
GLubyte patchData[2 * 2 * CHANNELS];
for (int i = 0; i < (int)sizeof(patchData); i += CHANNELS) {
patchData[i] = 0; patchData[i+1] = 255; patchData[i+2] = 0; patchData[i+3] = 255;
}
glTexSubImage3D(GL_TEXTURE_3D, 0, 1, 1, 2,
2, 2, 1,
GL_RGBA, GL_UNSIGNED_BYTE, patchData);
assert(glGetError() == GL_NO_ERROR);
printf("PASS: texSubImage3D patched 2x2 green rect at (1,1) in z-slice 2\n");
/* ---- readback setup ---- */
GLuint fb;
glGenFramebuffers(1, &fb);
GLubyte readBuf[WIDTH * HEIGHT * CHANNELS];
/* ---- verify slice 0: original ramp data ---- */
printf(" --- Slice 0 (original data) ---\n");
read_slice(fb, tex, 0, readBuf);
check_pixel(readBuf, 0, 0, 0, 0, 0, 255, "slice 0 pixel (0,0)");
check_pixel(readBuf, 3, 0, 0, 0, 192, 255, "slice 0 pixel (3,0)");
/* ---- verify slice 1: solid red ---- */
printf(" --- Slice 1 (solid red) ---\n");
read_slice(fb, tex, 1, readBuf);
for (int y = 0; y < HEIGHT; y++)
for (int x = 0; x < WIDTH; x++)
check_pixel(readBuf, x, y, 255, 0, 0, 255, "slice 1 red check");
/* ---- verify slice 2: green patch + original elsewhere ---- */
printf(" --- Slice 2 (2x2 green patch at 1,1) ---\n");
read_slice(fb, tex, 2, readBuf);
check_pixel(readBuf, 1, 1, 0, 255, 0, 255, "slice 2 patch (1,1)");
check_pixel(readBuf, 2, 1, 0, 255, 0, 255, "slice 2 patch (2,1)");
check_pixel(readBuf, 1, 2, 0, 255, 0, 255, "slice 2 patch (1,2)");
check_pixel(readBuf, 2, 2, 0, 255, 0, 255, "slice 2 patch (2,2)");
check_pixel(readBuf, 0, 0, 128, 0, 0, 255, "slice 2 corner (0,0) original");
/* ---- verify slice 3: untouched original ---- */
printf(" --- Slice 3 (original data) ---\n");
read_slice(fb, tex, 3, readBuf);
check_pixel(readBuf, 0, 0, 192, 0, 0, 255, "slice 3 pixel (0,0)");
/* ---- srcOffset test: upload from middle of a larger buffer ---- */
printf(" --- srcOffset variant (pointer arithmetic) ---\n");
GLubyte *bigBuf = (GLubyte *)malloc(sliceBytes * 2);
assert(bigBuf);
memset(bigBuf, 0, sliceBytes * 2);
for (int i = sliceBytes; i < sliceBytes * 2; i += CHANNELS) {
bigBuf[i] = 0; bigBuf[i+1] = 0; bigBuf[i+2] = 255; bigBuf[i+3] = 255;
}
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 3,
WIDTH, HEIGHT, 1,
GL_RGBA, GL_UNSIGNED_BYTE, bigBuf + sliceBytes);
assert(glGetError() == GL_NO_ERROR);
read_slice(fb, tex, 3, readBuf);
for (int y = 0; y < HEIGHT; y++)
for (int x = 0; x < WIDTH; x++)
check_pixel(readBuf, x, y, 0, 0, 255, 255, "slice 3 blue (srcOffset)");
printf("PASS: srcOffset variant — slice 3 is now solid blue\n");
/* ---- unpack alignment test ---- */
printf(" --- Unpack alignment ---\n");
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
GLubyte tightData[2 * 2 * CHANNELS];
for (int i = 0; i < (int)sizeof(tightData); i += CHANNELS) {
tightData[i] = 255; tightData[i+1] = 255; tightData[i+2] = 0; tightData[i+3] = 255;
}
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0,
2, 2, 1,
GL_RGBA, GL_UNSIGNED_BYTE, tightData);
assert(glGetError() == GL_NO_ERROR);
read_slice(fb, tex, 0, readBuf);
check_pixel(readBuf, 0, 0, 255, 255, 0, 255, "alignment: (0,0) yellow");
{
int i = (3 * WIDTH + 3) * CHANNELS;
int isYellow = (readBuf[i] == 255 && readBuf[i+1] == 255 &&
readBuf[i+2] == 0 && readBuf[i+3] == 255);
assert(!isYellow);
printf("PASS: alignment: (3,3) not overwritten — rgba(%d,%d,%d,%d)\n",
readBuf[i], readBuf[i+1], readBuf[i+2], readBuf[i+3]);
}
/* ---- cleanup ---- */
glDeleteFramebuffers(1, &fb);
glDeleteTextures(1, &tex);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
free(data);
free(sliceData);
free(bigBuf);
printf(" --- done ---\n");
return 0;
}