diff --git a/src/client/graphics/webgl_context.cpp b/src/client/graphics/webgl_context.cpp index d71e91c3c..ac224b851 100644 --- a/src/client/graphics/webgl_context.cpp +++ b/src/client/graphics/webgl_context.cpp @@ -1968,6 +1968,22 @@ namespace endor return -1; } + vector WebGL2Context::getInternalformatParameter(uint32_t target, uint32_t internalformat, uint32_t pname) + { + // Query the underlying GL implementation via command buffer + auto req = GetInternalformatParameterCommandBufferRequest(target, internalformat, pname); + sendCommandBufferRequest(req, true); + auto response = recvResponse( + COMMAND_BUFFER_GET_INTERNALFORMAT_PARAMETER_RES, + req); + + if (response == nullptr) [[unlikely]] + { + throw runtime_error("Failed to get internal format parameter: timeout."); + } + return response->values; + } + int WebGL2Context::getParameterV2(WebGL2IntegerParameterName pname) { /** diff --git a/src/client/graphics/webgl_context.hpp b/src/client/graphics/webgl_context.hpp index ae034207c..17433a9e4 100644 --- a/src/client/graphics/webgl_context.hpp +++ b/src/client/graphics/webgl_context.hpp @@ -917,6 +917,7 @@ namespace endor std::optional dstOffset = std::nullopt, std::optional length = std::nullopt); int getFragDataLocation(std::shared_ptr program, const std::string &name); + std::vector getInternalformatParameter(uint32_t target, uint32_t internalformat, uint32_t pname); int getParameterV2(WebGL2IntegerParameterName pname); std::shared_ptr getQuery(WebGLQueryTarget target, int pname); int getUniformBlockIndex(std::shared_ptr program, const std::string &uniformBlockName); diff --git a/src/client/script_bindings/webgl/webgl2_rendering_context.cpp b/src/client/script_bindings/webgl/webgl2_rendering_context.cpp index f6797d1d1..9d8ff9faf 100644 --- a/src/client/script_bindings/webgl/webgl2_rendering_context.cpp +++ b/src/client/script_bindings/webgl/webgl2_rendering_context.cpp @@ -418,6 +418,7 @@ namespace endor // Enhanced framebuffer operations ADD_WEBGL2_METHOD("blitFramebuffer", BlitFramebuffer) ADD_WEBGL2_METHOD("renderbufferStorageMultisample", RenderbufferStorageMultisample) + ADD_WEBGL2_METHOD("getInternalformatParameter", GetInternalformatParameter) ADD_WEBGL2_METHOD("framebufferTextureLayer", FramebufferTextureLayer) // Enhanced texture operations @@ -1703,6 +1704,56 @@ namespace endor args.GetReturnValue().SetUndefined(); } + void WebGL2RenderingContext::GetInternalformatParameter(const FunctionCallbackInfo &args) + { + Isolate *isolate = args.GetIsolate(); + HandleScope scope(isolate); + Local context = isolate->GetCurrentContext(); + + if (args.Length() < 3) + { + isolate->ThrowException(Exception::TypeError( + MakeMethodArgCountError(isolate, "getInternalformatParameter", 3, args.Length()))); + return; + } + + for (int i = 0; i < 3; ++i) + { + if (!args[i]->IsNumber()) + { + isolate->ThrowException(Exception::TypeError( + MakeMethodArgTypeError(isolate, "getInternalformatParameter", i, "number", args[i]))); + return; + } + } + + uint32_t target = args[0]->Uint32Value(context).ToChecked(); + uint32_t internalformat = args[1]->Uint32Value(context).ToChecked(); + uint32_t pname = args[2]->Uint32Value(context).ToChecked(); + + // Delegate to WebGL2Context for the actual query + auto values = handle()->getInternalformatParameter(target, internalformat, pname); + + // If empty vector returned, it means unsupported pname - return null per spec + if (values.empty()) + { + args.GetReturnValue().SetNull(); + return; + } + + // Create Int32Array to return the values + auto arraybuffer = ArrayBuffer::New(isolate, sizeof(int32_t) * values.size()); + auto backingStore = arraybuffer->GetBackingStore(); + int32_t *data = static_cast(backingStore->Data()); + for (size_t i = 0; i < values.size(); i++) + { + data[i] = values[i]; + } + + Local result = Int32Array::New(arraybuffer, 0, values.size()); + args.GetReturnValue().Set(result); + } + void WebGL2RenderingContext::FramebufferTextureLayer(const v8::FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); diff --git a/src/client/script_bindings/webgl/webgl2_rendering_context.hpp b/src/client/script_bindings/webgl/webgl2_rendering_context.hpp index faf38623b..a09b1060a 100644 --- a/src/client/script_bindings/webgl/webgl2_rendering_context.hpp +++ b/src/client/script_bindings/webgl/webgl2_rendering_context.hpp @@ -100,6 +100,7 @@ namespace endor // Enhanced framebuffer operations void BlitFramebuffer(const v8::FunctionCallbackInfo &args); void RenderbufferStorageMultisample(const v8::FunctionCallbackInfo &args); + void GetInternalformatParameter(const v8::FunctionCallbackInfo &args); void FramebufferTextureLayer(const v8::FunctionCallbackInfo &args); // Enhanced texture operations diff --git a/src/common/command_buffers/details/properties.hpp b/src/common/command_buffers/details/properties.hpp index 854ed4466..b2341092b 100644 --- a/src/common/command_buffers/details/properties.hpp +++ b/src/common/command_buffers/details/properties.hpp @@ -207,4 +207,76 @@ namespace commandbuffers public: int error; }; + + class GetInternalformatParameterCommandBufferRequest final + : public TrCommandBufferSimpleRequest + { + public: + GetInternalformatParameterCommandBufferRequest() = delete; + GetInternalformatParameterCommandBufferRequest(uint32_t target, uint32_t internalformat, uint32_t pname) + : TrCommandBufferSimpleRequest() + , target(target) + , internalformat(internalformat) + , pname(pname) + { + } + GetInternalformatParameterCommandBufferRequest(const GetInternalformatParameterCommandBufferRequest &that, + bool clone = false) + : TrCommandBufferSimpleRequest(that, clone) + , target(that.target) + , internalformat(that.internalformat) + , pname(that.pname) + { + } + + public: + uint32_t target; + uint32_t internalformat; + uint32_t pname; + }; + + class GetInternalformatParameterCommandBufferResponse final + : public TrCommandBufferSimpleResponse + { + public: + GetInternalformatParameterCommandBufferResponse() = delete; + GetInternalformatParameterCommandBufferResponse(GetInternalformatParameterCommandBufferRequest *req, + const std::vector &values) + : TrCommandBufferSimpleResponse(COMMAND_BUFFER_GET_INTERNALFORMAT_PARAMETER_RES, req) + , values(values) + { + } + GetInternalformatParameterCommandBufferResponse(const GetInternalformatParameterCommandBufferResponse &that, + bool clone = false) + : TrCommandBufferSimpleResponse(that, clone) + { + if (clone) + values = that.values; + } + + public: + TrCommandBufferMessage *serialize() override + { + auto message = new TrCommandBufferMessage(type, size, this); + if (values.size() > 0) + { + // Serialize the vector using addVecSegment + message->addVecSegment(values); + } + return message; + } + + void deserialize(TrCommandBufferMessage &message) override + { + auto valuesSegment = message.nextSegment(); + if (valuesSegment != nullptr) + values = valuesSegment->toVec(); + else + values.clear(); + } + + public: + std::vector values; + }; } diff --git a/src/common/command_buffers/macros.hpp b/src/common/command_buffers/macros.hpp index 15bad43b4..07ed8094d 100644 --- a/src/common/command_buffers/macros.hpp +++ b/src/common/command_buffers/macros.hpp @@ -148,6 +148,7 @@ XX(SAMPLER_PARAMETERF, SamplerParameterfCommandBufferRequest, "GL::SamplerParameterf") \ XX(GET_SAMPLER_PARAMETER, GetSamplerParameterCommandBufferRequest, "GL::GetSamplerParameter") \ XX(IS_SAMPLER, IsSamplerCommandBufferRequest, "GL::IsSampler") \ + XX(GET_INTERNALFORMAT_PARAMETER, GetInternalformatParameterCommandBufferRequest, "GL::GetInternalformatParameter") \ XX(XRFRAME_START, XRFrameStartCommandBufferRequest, "XR::FrameStart") \ XX(XRFRAME_FLUSH, XRFrameFlushCommandBufferRequest, "XR::FrameFlush") \ XX(XRFRAME_END, XRFrameEndCommandBufferRequest, "XR::FrameEnd") \ @@ -171,4 +172,5 @@ XX(GET_FLOATV, GetFloatvCommandBufferResponse, "GL::GetFloatv") \ XX(GET_STRING, GetStringCommandBufferResponse, "GL::GetString") \ XX(GET_SHADER_PRECISION_FORMAT, GetShaderPrecisionFormatCommandBufferResponse, "GL::GetShaderPrecisionFormat") \ - XX(GET_ERROR, GetErrorCommandBufferResponse, "GL::GetError") + XX(GET_ERROR, GetErrorCommandBufferResponse, "GL::GetError") \ + XX(GET_INTERNALFORMAT_PARAMETER, GetInternalformatParameterCommandBufferResponse, "GL::GetInternalformatParameter") diff --git a/src/common/command_buffers/shared.hpp b/src/common/command_buffers/shared.hpp index a6a240567..9af627f16 100644 --- a/src/common/command_buffers/shared.hpp +++ b/src/common/command_buffers/shared.hpp @@ -233,6 +233,8 @@ namespace commandbuffers COMMAND_BUFFER_GET_SHADER_PRECISION_FORMAT_RES, COMMAND_BUFFER_GET_ERROR_REQ, COMMAND_BUFFER_GET_ERROR_RES, + COMMAND_BUFFER_GET_INTERNALFORMAT_PARAMETER_REQ, + COMMAND_BUFFER_GET_INTERNALFORMAT_PARAMETER_RES, /** XRFrame controls */ COMMAND_BUFFER_XRFRAME_START_REQ, diff --git a/src/renderer/render_api_opengles.cpp b/src/renderer/render_api_opengles.cpp index 5459a69e5..ffac3d4e3 100644 --- a/src/renderer/render_api_opengles.cpp +++ b/src/renderer/render_api_opengles.cpp @@ -2689,6 +2689,63 @@ class RHI_OpenGL : public TrRenderHardwareInterface DEBUG(DEBUG_TAG, "[%d] GL::GetError() => %d", options.isDefaultQueue(), res.error); reqContentRenderer->sendCommandBufferResponse(res); } + TR_OPENGL_FUNC void OnGetInternalformatParameter(GetInternalformatParameterCommandBufferRequest *req, + renderer::TrContentRenderer *reqContentRenderer, + ApiCallOptions &options) + { + std::vector values; + + // Support WEBGL_SAMPLES for now, log error for others + if (req->pname == WEBGL_SAMPLES) + { + GLint numSampleCounts = 0; + glGetInternalformativ(req->target, req->internalformat, GL_NUM_SAMPLE_COUNTS, 1, &numSampleCounts); + GLenum error = glGetError(); + if (error == GL_NO_ERROR && numSampleCounts > 0) + { + std::vector samples(numSampleCounts); + glGetInternalformativ(req->target, req->internalformat, GL_SAMPLES, numSampleCounts, samples.data()); + error = glGetError(); + + if (error == GL_NO_ERROR) + { + for (GLint sample : samples) + { + if (sample > 0) + values.push_back(static_cast(sample)); + } + std::sort(values.begin(), values.end(), std::greater()); + } + } + if (values.empty()) + { + DEBUG(DEBUG_TAG, "[GetInternalformatParameter] Using fallback values {4, 2, 1}"); + values = {4, 2, 1}; + } + } + else + { + // Not implemented for other pnames + DEBUG(LOG_TAG_ERROR, + "[GetInternalformatParameter] pname 0x%x is not supported.", + req->pname); + } + + GetInternalformatParameterCommandBufferResponse res(req, values); + // Note: CheckError is called here but won't detect errors from glGetInternalformativ + // calls above since those errors are already consumed by explicit glGetError() calls + if (TR_UNLIKELY(CheckError(req, reqContentRenderer) != GL_NO_ERROR || options.printsCall)) + { + DEBUG(DEBUG_TAG, + "[%d] GL::GetInternalformativ(0x%x, 0x%x, 0x%x) => [%zu values]", + options.isDefaultQueue(), + req->target, + req->internalformat, + req->pname, + values.size()); + } + reqContentRenderer->sendCommandBufferResponse(res); + } }; void RHI_OpenGL::ProcessDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces *interfaces) @@ -3026,6 +3083,9 @@ bool RHI_OpenGL::ExecuteCommandBuffer(vector