mirror of
https://github.com/godotengine/godot.git
synced 2024-11-24 21:22:48 +00:00
Add multiview to the opengl3 driver
This commit is contained in:
parent
a8c805be29
commit
398ee08375
@ -274,15 +274,32 @@ RasterizerGLES3::~RasterizerGLES3() {
|
||||
void RasterizerGLES3::prepare_for_blitting_render_targets() {
|
||||
}
|
||||
|
||||
void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect) {
|
||||
void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer) {
|
||||
GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_render_target);
|
||||
|
||||
ERR_FAIL_COND(!rt);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
|
||||
GLuint read_fbo = 0;
|
||||
if (rt->view_count > 1) {
|
||||
glGenFramebuffers(1, &read_fbo);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo);
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, p_layer);
|
||||
} else {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
|
||||
}
|
||||
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
|
||||
// Flip content upside down to correct for coordinates.
|
||||
glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, 0, p_screen_rect.size.y, p_screen_rect.size.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
Vector2i screen_rect_end = p_screen_rect.get_end();
|
||||
glBlitFramebuffer(0, 0, rt->size.x, rt->size.y,
|
||||
p_screen_rect.position.x, screen_rect_end.y, screen_rect_end.x, p_screen_rect.position.y,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
if (read_fbo != 0) {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &read_fbo);
|
||||
}
|
||||
}
|
||||
|
||||
// is this p_screen useless in a multi window environment?
|
||||
@ -293,7 +310,7 @@ void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_sc
|
||||
RID rid_rt = blit.render_target;
|
||||
|
||||
Rect2 dst_rect = blit.dst_rect;
|
||||
_blit_render_target_to_screen(rid_rt, p_screen, dst_rect);
|
||||
_blit_render_target_to_screen(rid_rt, p_screen, dst_rect, blit.multi_view.use_layer ? blit.multi_view.layer : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ protected:
|
||||
RasterizerCanvasGLES3 *canvas = nullptr;
|
||||
RasterizerSceneGLES3 *scene = nullptr;
|
||||
|
||||
void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect);
|
||||
void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer);
|
||||
|
||||
public:
|
||||
RendererUtilities *get_utilities() { return utilities; }
|
||||
|
@ -1273,6 +1273,19 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da
|
||||
GLES3::MaterialStorage::store_transform(p_render_data->cam_transform, scene_state.ubo.inv_view_matrix);
|
||||
GLES3::MaterialStorage::store_transform(p_render_data->inv_cam_transform, scene_state.ubo.view_matrix);
|
||||
|
||||
if (p_render_data->view_count > 1) {
|
||||
for (uint32_t v = 0; v < p_render_data->view_count; v++) {
|
||||
projection = correction * p_render_data->view_projection[v];
|
||||
GLES3::MaterialStorage::store_camera(projection, scene_state.multiview_ubo.projection_matrix_view[v]);
|
||||
GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.multiview_ubo.inv_projection_matrix_view[v]);
|
||||
|
||||
scene_state.multiview_ubo.eye_offset[v][0] = p_render_data->view_eye_offset[v].x;
|
||||
scene_state.multiview_ubo.eye_offset[v][1] = p_render_data->view_eye_offset[v].y;
|
||||
scene_state.multiview_ubo.eye_offset[v][2] = p_render_data->view_eye_offset[v].z;
|
||||
scene_state.multiview_ubo.eye_offset[v][3] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
scene_state.ubo.directional_light_count = p_render_data->directional_light_count;
|
||||
|
||||
scene_state.ubo.z_far = p_render_data->z_far;
|
||||
@ -1374,6 +1387,15 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_DATA_UNIFORM_LOCATION, scene_state.ubo_buffer);
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::UBO), &scene_state.ubo, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
|
||||
if (p_render_data->view_count > 1) {
|
||||
if (scene_state.multiview_buffer == 0) {
|
||||
glGenBuffers(1, &scene_state.multiview_buffer);
|
||||
}
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer);
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::MultiviewUBO), &scene_state.multiview_ubo, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Puts lights into Uniform Buffers. Needs to be called before _fill_list as this caches the index of each light in the Uniform Buffer
|
||||
@ -1916,8 +1938,14 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
||||
GLES3::SceneShaderData *prev_shader = nullptr;
|
||||
GeometryInstanceGLES3 *prev_inst = nullptr;
|
||||
SceneShaderGLES3::ShaderVariant prev_variant = SceneShaderGLES3::ShaderVariant::MODE_COLOR;
|
||||
SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized
|
||||
|
||||
SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized.
|
||||
// @todo Get this from p_params->spec_constant_base_flags instead of hardcoding it.
|
||||
uint32_t base_spec_constants = 0;
|
||||
|
||||
if (p_render_data->view_count > 1) {
|
||||
base_spec_constants |= 1 << SPEC_CONSTANT_USE_MULTIVIEW;
|
||||
}
|
||||
|
||||
switch (p_pass_mode) {
|
||||
case PASS_MODE_COLOR:
|
||||
@ -1957,8 +1985,6 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
||||
continue;
|
||||
}
|
||||
|
||||
//uint32_t base_spec_constants = p_params->spec_constant_base_flags;
|
||||
|
||||
GLES3::SceneShaderData *shader;
|
||||
GLES3::SceneMaterialData *material_data;
|
||||
void *mesh_surface;
|
||||
@ -2128,7 +2154,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
||||
}
|
||||
|
||||
if (prev_shader != shader || prev_variant != instance_variant) {
|
||||
material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant);
|
||||
material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant, base_spec_constants);
|
||||
float opaque_prepass_threshold = 0.0;
|
||||
if constexpr (p_pass_mode == PASS_MODE_DEPTH) {
|
||||
opaque_prepass_threshold = 0.99;
|
||||
@ -2136,7 +2162,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
||||
opaque_prepass_threshold = 0.1;
|
||||
}
|
||||
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant);
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant, base_spec_constants);
|
||||
|
||||
prev_shader = shader;
|
||||
prev_variant = instance_variant;
|
||||
@ -2144,21 +2170,21 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
||||
|
||||
if (prev_inst != inst || prev_shader != shader || prev_variant != instance_variant) {
|
||||
// Rebind the light indices.
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, instance_variant);
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, instance_variant);
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, instance_variant, base_spec_constants);
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, instance_variant, base_spec_constants);
|
||||
|
||||
if (inst->omni_light_count) {
|
||||
glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, instance_variant), inst->omni_light_count, inst->omni_light_gl_cache.ptr());
|
||||
glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, instance_variant, base_spec_constants), inst->omni_light_count, inst->omni_light_gl_cache.ptr());
|
||||
}
|
||||
|
||||
if (inst->spot_light_count) {
|
||||
glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant), inst->spot_light_count, inst->spot_light_gl_cache.ptr());
|
||||
glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant, base_spec_constants), inst->spot_light_count, inst->spot_light_gl_cache.ptr());
|
||||
}
|
||||
|
||||
prev_inst = inst;
|
||||
}
|
||||
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant);
|
||||
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, base_spec_constants);
|
||||
if (inst->instance_count > 0) {
|
||||
// Using MultiMesh.
|
||||
// Bind instance buffers.
|
||||
|
@ -74,6 +74,7 @@ enum SceneUniformLocation {
|
||||
SCENE_OMNILIGHT_UNIFORM_LOCATION,
|
||||
SCENE_SPOTLIGHT_UNIFORM_LOCATION,
|
||||
SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION,
|
||||
SCENE_MULTIVIEW_UNIFORM_LOCATION,
|
||||
};
|
||||
|
||||
enum SkyUniformLocation {
|
||||
@ -90,6 +91,8 @@ enum {
|
||||
SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 2,
|
||||
SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 3,
|
||||
SPEC_CONSTANT_DISABLE_FOG = 4,
|
||||
SPEC_CONSTANT_USE_RADIANCE_MAP = 5,
|
||||
SPEC_CONSTANT_USE_MULTIVIEW = 6,
|
||||
};
|
||||
|
||||
struct RenderDataGLES3 {
|
||||
@ -343,6 +346,13 @@ private:
|
||||
};
|
||||
static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes");
|
||||
|
||||
struct MultiviewUBO {
|
||||
float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
|
||||
float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
|
||||
float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4];
|
||||
};
|
||||
static_assert(sizeof(MultiviewUBO) % 16 == 0, "Multiview UBO size must be a multiple of 16 bytes");
|
||||
|
||||
struct TonemapUBO {
|
||||
float exposure = 1.0;
|
||||
float white = 1.0;
|
||||
@ -353,6 +363,8 @@ private:
|
||||
|
||||
UBO ubo;
|
||||
GLuint ubo_buffer = 0;
|
||||
MultiviewUBO multiview_ubo;
|
||||
GLuint multiview_buffer = 0;
|
||||
GLuint tonemap_buffer = 0;
|
||||
|
||||
bool used_depth_prepass = false;
|
||||
|
@ -142,7 +142,7 @@ RID ShaderGLES3::version_create() {
|
||||
return version_owner.make_rid(version);
|
||||
}
|
||||
|
||||
void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization) {
|
||||
void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization) {
|
||||
#ifdef GLES_OVER_GL
|
||||
builder.append("#version 330\n");
|
||||
builder.append("#define USE_GLES_OVER_GL\n");
|
||||
@ -171,6 +171,24 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant
|
||||
}
|
||||
builder.append("\n"); //make sure defines begin at newline
|
||||
|
||||
// Insert multiview extension loading, because it needs to appear before
|
||||
// any non-preprocessor code (like the "precision highp..." lines below).
|
||||
builder.append("#ifdef USE_MULTIVIEW\n");
|
||||
builder.append("#if defined(GL_OVR_multiview2)\n");
|
||||
builder.append("#extension GL_OVR_multiview2 : require\n");
|
||||
builder.append("#elif defined(GL_OVR_multiview)\n");
|
||||
builder.append("#extension GL_OVR_multiview : require\n");
|
||||
builder.append("#endif\n");
|
||||
if (p_stage_type == StageType::STAGE_TYPE_VERTEX) {
|
||||
builder.append("layout(num_views=2) in;\n");
|
||||
}
|
||||
builder.append("#define ViewIndex gl_ViewID_OVR\n");
|
||||
builder.append("#define MAX_VIEWS 2\n");
|
||||
builder.append("#else\n");
|
||||
builder.append("#define ViewIndex 0\n");
|
||||
builder.append("#define MAX_VIEWS 1\n");
|
||||
builder.append("#endif\n");
|
||||
|
||||
// Default to highp precision unless specified otherwise.
|
||||
builder.append("precision highp float;\n");
|
||||
builder.append("precision highp int;\n");
|
||||
@ -180,8 +198,9 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant
|
||||
builder.append("precision highp sampler2DArray;\n");
|
||||
#endif
|
||||
|
||||
for (uint32_t i = 0; i < p_template.chunks.size(); i++) {
|
||||
const StageTemplate::Chunk &chunk = p_template.chunks[i];
|
||||
const StageTemplate &stage_template = stage_templates[p_stage_type];
|
||||
for (uint32_t i = 0; i < stage_template.chunks.size(); i++) {
|
||||
const StageTemplate::Chunk &chunk = stage_template.chunks[i];
|
||||
switch (chunk.type) {
|
||||
case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: {
|
||||
builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment)
|
||||
@ -224,7 +243,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_
|
||||
//vertex stage
|
||||
{
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX], p_specialization);
|
||||
_build_variant_code(builder, p_variant, p_version, STAGE_TYPE_VERTEX, p_specialization);
|
||||
|
||||
spec.vert_id = glCreateShader(GL_VERTEX_SHADER);
|
||||
String builder_string = builder.as_string();
|
||||
@ -272,7 +291,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_
|
||||
//fragment stage
|
||||
{
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT], p_specialization);
|
||||
_build_variant_code(builder, p_variant, p_version, STAGE_TYPE_FRAGMENT, p_specialization);
|
||||
|
||||
spec.frag_id = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
String builder_string = builder.as_string();
|
||||
@ -413,7 +432,7 @@ RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_ver
|
||||
|
||||
{
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX], specialization_default_mask);
|
||||
_build_variant_code(builder, i, version, STAGE_TYPE_VERTEX, specialization_default_mask);
|
||||
|
||||
RS::ShaderNativeSourceCode::Version::Stage stage;
|
||||
stage.name = "vertex";
|
||||
@ -425,7 +444,7 @@ RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_ver
|
||||
//fragment stage
|
||||
{
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT], specialization_default_mask);
|
||||
_build_variant_code(builder, i, version, STAGE_TYPE_FRAGMENT, specialization_default_mask);
|
||||
|
||||
RS::ShaderNativeSourceCode::Version::Stage stage;
|
||||
stage.name = "fragment";
|
||||
|
@ -153,7 +153,7 @@ private:
|
||||
|
||||
StageTemplate stage_templates[STAGE_TYPE_MAX];
|
||||
|
||||
void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization);
|
||||
void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization);
|
||||
|
||||
void _add_stage(const char *p_code, StageType p_stage_type);
|
||||
|
||||
|
@ -16,6 +16,7 @@ DISABLE_LIGHT_OMNI = false
|
||||
DISABLE_LIGHT_SPOT = false
|
||||
DISABLE_FOG = false
|
||||
USE_RADIANCE_MAP = true
|
||||
USE_MULTIVIEW = false
|
||||
|
||||
|
||||
#[vertex]
|
||||
@ -153,6 +154,15 @@ layout(std140) uniform SceneData { // ubo:2
|
||||
}
|
||||
scene_data;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(std140) uniform MultiviewData { // ubo:8
|
||||
highp mat4 projection_matrix_view[MAX_VIEWS];
|
||||
highp mat4 inv_projection_matrix_view[MAX_VIEWS];
|
||||
highp vec4 eye_offset[MAX_VIEWS];
|
||||
}
|
||||
multiview_data;
|
||||
#endif
|
||||
|
||||
uniform highp mat4 world_transform;
|
||||
|
||||
#ifdef USE_LIGHTMAP
|
||||
@ -250,8 +260,14 @@ void main() {
|
||||
#if defined(OVERRIDE_POSITION)
|
||||
highp vec4 position;
|
||||
#endif
|
||||
highp mat4 projection_matrix = scene_data.projection_matrix;
|
||||
highp mat4 inv_projection_matrix = scene_data.inv_projection_matrix;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
mat4 projection_matrix = multiview_data.projection_matrix_view[ViewIndex];
|
||||
mat4 inv_projection_matrix = multiview_data.inv_projection_matrix_view[ViewIndex];
|
||||
#else
|
||||
mat4 projection_matrix = scene_data.projection_matrix;
|
||||
mat4 inv_projection_matrix = scene_data.inv_projection_matrix;
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#ifdef USE_INSTANCING
|
||||
vec4 instance_custom = vec4(unpackHalf2x16(instance_color_custom_data.z), unpackHalf2x16(instance_color_custom_data.w));
|
||||
@ -339,7 +355,6 @@ void main() {
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
|
||||
// Default to SPECULAR_SCHLICK_GGX.
|
||||
#if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON)
|
||||
#define SPECULAR_SCHLICK_GGX
|
||||
@ -463,6 +478,15 @@ layout(std140) uniform SceneData { // ubo:2
|
||||
}
|
||||
scene_data;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(std140) uniform MultiviewData { // ubo:8
|
||||
highp mat4 projection_matrix_view[MAX_VIEWS];
|
||||
highp mat4 inv_projection_matrix_view[MAX_VIEWS];
|
||||
highp vec4 eye_offset[MAX_VIEWS];
|
||||
}
|
||||
multiview_data;
|
||||
#endif
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#GLOBALS
|
||||
@ -530,8 +554,13 @@ uniform highp samplerCubeShadow positional_shadow; // texunit:-4
|
||||
|
||||
#endif // !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT)
|
||||
|
||||
uniform highp sampler2D screen_texture; // texunit:-5
|
||||
#ifdef USE_MULTIVIEW
|
||||
uniform highp sampler2DArray depth_buffer; // texunit:-6
|
||||
uniform highp sampler2DArray screen_texture; // texunit:-5
|
||||
#else
|
||||
uniform highp sampler2D depth_buffer; // texunit:-6
|
||||
uniform highp sampler2D screen_texture; // texunit:-5
|
||||
#endif
|
||||
|
||||
uniform highp mat4 world_transform;
|
||||
uniform mediump float opaque_prepass_threshold;
|
||||
@ -884,7 +913,11 @@ vec4 fog_process(vec3 vertex) {
|
||||
void main() {
|
||||
//lay out everything, whatever is unused is optimized away anyway
|
||||
vec3 vertex = vertex_interp;
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 view = -normalize(vertex_interp - multiview_data.eye_offset[ViewIndex].xyz);
|
||||
#else
|
||||
vec3 view = -normalize(vertex_interp);
|
||||
#endif
|
||||
vec3 albedo = vec3(1.0);
|
||||
vec3 backlight = vec3(0.0);
|
||||
vec4 transmittance_color = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
@ -44,7 +44,11 @@ in vec2 uv_interp;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
uniform highp sampler2DArray source; //texunit:0
|
||||
#else
|
||||
uniform highp sampler2D source; //texunit:0
|
||||
#endif
|
||||
|
||||
#if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7)
|
||||
#define USING_GLOW // only use glow when at least one glow level is selected
|
||||
@ -191,10 +195,17 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) {
|
||||
const float FXAA_REDUCE_MUL = (1.0 / 8.0);
|
||||
const float FXAA_SPAN_MAX = 8.0;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 rgbNW = textureLod(source, vec3(uv_interp + vec2(-1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz;
|
||||
vec3 rgbNE = textureLod(source, vec3(uv_interp + vec2(1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz;
|
||||
vec3 rgbSW = textureLod(source, vec3(uv_interp + vec2(-1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz;
|
||||
vec3 rgbSE = textureLod(source, vec3(uv_interp + vec2(1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz;
|
||||
#else
|
||||
vec3 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0).xyz;
|
||||
vec3 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0).xyz;
|
||||
vec3 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0).xyz;
|
||||
vec3 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0).xyz;
|
||||
#endif
|
||||
vec3 rgbM = color;
|
||||
vec3 luma = vec3(0.299, 0.587, 0.114);
|
||||
float lumaNW = dot(rgbNW, luma);
|
||||
@ -219,8 +230,13 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) {
|
||||
dir * rcpDirMin)) *
|
||||
pixel_size;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 rgbA = 0.5 * (textureLod(source, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz);
|
||||
vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz);
|
||||
#else
|
||||
vec3 rgbA = 0.5 * (textureLod(source, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz);
|
||||
vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source, uv_interp + dir * 0.5, 0.0).xyz);
|
||||
#endif
|
||||
|
||||
float lumaB = dot(rgbB, luma);
|
||||
if ((lumaB < lumaMin) || (lumaB > lumaMax)) {
|
||||
@ -231,7 +247,11 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec4 color = textureLod(source, vec3(uv_interp, ViewIndex), 0.0);
|
||||
#else
|
||||
vec4 color = textureLod(source, uv_interp, 0.0);
|
||||
#endif
|
||||
|
||||
#ifdef USE_FXAA
|
||||
color.rgb = apply_fxaa(color.rgb, uv_interp, pixel_size);
|
||||
|
@ -34,6 +34,15 @@
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/templates/vector.h"
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
#include <GLES3/gl3platform.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#endif
|
||||
|
||||
using namespace GLES3;
|
||||
|
||||
#define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
|
||||
@ -98,6 +107,16 @@ Config::Config() {
|
||||
anisotropic_level = MIN(float(1 << int(ProjectSettings::get_singleton()->get("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level);
|
||||
}
|
||||
|
||||
multiview_supported = extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview");
|
||||
#ifdef ANDROID_ENABLED
|
||||
if (multiview_supported) {
|
||||
eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR");
|
||||
if (eglFramebufferTextureMultiviewOVR == nullptr) {
|
||||
multiview_supported = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
|
||||
use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter");
|
||||
|
||||
|
@ -44,6 +44,10 @@
|
||||
#include OPENGL_INCLUDE_H
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei);
|
||||
#endif
|
||||
|
||||
namespace GLES3 {
|
||||
|
||||
class Config {
|
||||
@ -82,6 +86,11 @@ public:
|
||||
bool support_anisotropic_filter = false;
|
||||
float anisotropic_level = 0.0f;
|
||||
|
||||
bool multiview_supported = false;
|
||||
#ifdef ANDROID_ENABLED
|
||||
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
|
||||
#endif
|
||||
|
||||
static Config *get_singleton() { return singleton; };
|
||||
|
||||
Config();
|
||||
|
@ -33,12 +33,17 @@
|
||||
#include "render_scene_buffers_gles3.h"
|
||||
#include "texture_storage.h"
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR
|
||||
#endif
|
||||
|
||||
RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() {
|
||||
free_render_buffer_data();
|
||||
}
|
||||
|
||||
void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
|
||||
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
|
||||
GLES3::Config *config = GLES3::Config::get_singleton();
|
||||
|
||||
//internal_size.x = p_internal_size.x; // ignore for now
|
||||
//internal_size.y = p_internal_size.y;
|
||||
@ -50,7 +55,7 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte
|
||||
//msaa = p_msaa;
|
||||
//screen_space_aa = p_screen_space_aa;
|
||||
//use_debanding = p_use_debanding;
|
||||
//view_count = p_view_count;
|
||||
view_count = p_view_count;
|
||||
|
||||
free_render_buffer_data();
|
||||
|
||||
@ -62,24 +67,43 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, rt->color);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
|
||||
if (view_count > 1 && config->multiview_supported) {
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, rt->color);
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, view_count);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, rt->color);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
|
||||
}
|
||||
|
||||
glGenTextures(1, &depth_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, depth_texture);
|
||||
if (view_count > 1 && config->multiview_supported) {
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, depth_texture);
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, depth_texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0);
|
||||
if (view_count > 1 && config->multiview_supported) {
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_texture, 0, 0, view_count);
|
||||
} else {
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0);
|
||||
}
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
|
@ -56,7 +56,7 @@ public:
|
||||
RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
|
||||
//RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED;
|
||||
//bool use_debanding = false;
|
||||
//uint32_t view_count = 1;
|
||||
uint32_t view_count = 1;
|
||||
|
||||
bool is_transparent = false;
|
||||
|
||||
|
@ -34,6 +34,10 @@
|
||||
#include "config.h"
|
||||
#include "drivers/gles3/effects/copy_effects.h"
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR
|
||||
#endif
|
||||
|
||||
using namespace GLES3;
|
||||
|
||||
TextureStorage *TextureStorage::singleton = nullptr;
|
||||
@ -720,8 +724,7 @@ void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) {
|
||||
}
|
||||
|
||||
void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer) {
|
||||
// only 1 layer so far
|
||||
texture_set_data(p_texture, p_image);
|
||||
texture_set_data(p_texture, p_image, p_layer);
|
||||
#ifdef TOOLS_ENABLED
|
||||
Texture *tex = texture_owner.get_or_null(p_texture);
|
||||
|
||||
@ -1012,7 +1015,7 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image,
|
||||
img->resize_to_po2(false);
|
||||
}
|
||||
|
||||
GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : GL_TEXTURE_2D;
|
||||
GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : texture->target;
|
||||
|
||||
Vector<uint8_t> read = img->get_data();
|
||||
|
||||
@ -1069,7 +1072,11 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image,
|
||||
glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]);
|
||||
} else {
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]);
|
||||
if (texture->target == GL_TEXTURE_2D_ARRAY) {
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 0, format, type, &read[ofs]);
|
||||
} else {
|
||||
glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]);
|
||||
}
|
||||
}
|
||||
|
||||
tsize += size;
|
||||
@ -1425,6 +1432,8 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
|
||||
return;
|
||||
}
|
||||
|
||||
Config *config = Config::get_singleton();
|
||||
|
||||
rt->color_internal_format = rt->is_transparent ? GL_RGBA8 : GL_RGB10_A2;
|
||||
rt->color_format = GL_RGBA;
|
||||
rt->color_type = rt->is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
@ -1446,17 +1455,29 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
|
||||
|
||||
// color
|
||||
glGenTextures(1, &rt->color);
|
||||
glBindTexture(GL_TEXTURE_2D, rt->color);
|
||||
if (rt->view_count > 1 && config->multiview_supported) {
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, rt->color);
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, rt->color_internal_format, rt->size.x, rt->size.y, rt->view_count, 0, rt->color_format, rt->color_type, nullptr);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, rt->color);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
|
||||
if (rt->view_count > 1 && config->multiview_supported) {
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, rt->view_count);
|
||||
} else {
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
|
||||
}
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
|
||||
@ -1475,8 +1496,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
|
||||
|
||||
texture->format = rt->image_format;
|
||||
texture->real_format = rt->image_format;
|
||||
texture->type = Texture::TYPE_2D;
|
||||
texture->target = GL_TEXTURE_2D;
|
||||
if (rt->view_count > 1 && config->multiview_supported) {
|
||||
texture->type = Texture::TYPE_LAYERED;
|
||||
texture->target = GL_TEXTURE_2D_ARRAY;
|
||||
texture->layers = rt->view_count;
|
||||
} else {
|
||||
texture->type = Texture::TYPE_2D;
|
||||
texture->target = GL_TEXTURE_2D;
|
||||
texture->layers = 1;
|
||||
}
|
||||
texture->gl_format_cache = rt->color_format;
|
||||
texture->gl_type_cache = GL_UNSIGNED_BYTE;
|
||||
texture->gl_internal_format_cache = rt->color_internal_format;
|
||||
@ -1619,13 +1647,14 @@ void TextureStorage::render_target_set_size(RID p_render_target, int p_width, in
|
||||
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
|
||||
ERR_FAIL_COND(!rt);
|
||||
|
||||
if (p_width == rt->size.x && p_height == rt->size.y) {
|
||||
if (p_width == rt->size.x && p_height == rt->size.y && p_view_count == rt->view_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
_clear_render_target(rt);
|
||||
|
||||
rt->size = Size2i(p_width, p_height);
|
||||
rt->view_count = p_view_count;
|
||||
|
||||
_update_render_target(rt);
|
||||
}
|
||||
|
@ -324,6 +324,7 @@ private:
|
||||
struct RenderTarget {
|
||||
Point2i position = Point2i(0, 0);
|
||||
Size2i size = Size2i(0, 0);
|
||||
uint32_t view_count = 1;
|
||||
int mipmap_count = 1;
|
||||
RID self;
|
||||
GLuint fbo = 0;
|
||||
|
@ -65,7 +65,7 @@ extern int godot_webxr_get_view_count();
|
||||
extern int *godot_webxr_get_render_target_size();
|
||||
extern float *godot_webxr_get_transform_for_eye(int p_eye);
|
||||
extern float *godot_webxr_get_projection_for_eye(int p_eye);
|
||||
extern void godot_webxr_commit_for_eye(int p_eye, unsigned int p_destination_fbo);
|
||||
extern void godot_webxr_commit(unsigned int p_texture);
|
||||
|
||||
extern void godot_webxr_sample_controller_data();
|
||||
extern int godot_webxr_get_controller_count();
|
||||
|
@ -337,20 +337,18 @@ const GodotWebXR = {
|
||||
return buf;
|
||||
},
|
||||
|
||||
godot_webxr_commit_for_eye__proxy: 'sync',
|
||||
godot_webxr_commit_for_eye__sig: 'vii',
|
||||
godot_webxr_commit_for_eye: function (p_eye, p_destination_fbo) {
|
||||
godot_webxr_commit__proxy: 'sync',
|
||||
godot_webxr_commit__sig: 'vi',
|
||||
godot_webxr_commit: function (p_texture) {
|
||||
if (!GodotWebXR.session || !GodotWebXR.pose) {
|
||||
return;
|
||||
}
|
||||
|
||||
const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0;
|
||||
const glLayer = GodotWebXR.session.renderState.baseLayer;
|
||||
const view = GodotWebXR.pose.views[view_index];
|
||||
const viewport = glLayer.getViewport(view);
|
||||
const views = GodotWebXR.pose.views;
|
||||
const gl = GodotWebXR.gl;
|
||||
|
||||
const framebuffer = GL.framebuffers[p_destination_fbo];
|
||||
const texture = GL.textures[p_texture];
|
||||
|
||||
const orig_framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
|
||||
const orig_read_framebuffer = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING);
|
||||
@ -359,14 +357,27 @@ const GodotWebXR = {
|
||||
|
||||
// Copy from Godot render target into framebuffer from WebXR.
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, framebuffer);
|
||||
gl.readBuffer(gl.COLOR_ATTACHMENT0);
|
||||
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, glLayer.framebuffer);
|
||||
for (let i = 0; i < views.length; i++) {
|
||||
const viewport = glLayer.getViewport(views[i]);
|
||||
|
||||
// Flip Y upside down on destination.
|
||||
gl.blitFramebuffer(0, 0, viewport.width, viewport.height,
|
||||
viewport.x, viewport.height, viewport.width, viewport.y,
|
||||
gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
||||
const read_fbo = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, read_fbo);
|
||||
if (views.length > 1) {
|
||||
gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture, 0, i);
|
||||
} else {
|
||||
gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
||||
}
|
||||
gl.readBuffer(gl.COLOR_ATTACHMENT0);
|
||||
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, glLayer.framebuffer);
|
||||
|
||||
// Flip Y upside down on destination.
|
||||
gl.blitFramebuffer(0, 0, viewport.width, viewport.height,
|
||||
viewport.x, viewport.y + viewport.height, viewport.x + viewport.width, viewport.y,
|
||||
gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
||||
|
||||
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
|
||||
gl.deleteFramebuffer(read_fbo);
|
||||
}
|
||||
|
||||
// Restore state.
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, orig_framebuffer);
|
||||
|
@ -415,8 +415,7 @@ Vector<BlitToScreen> WebXRInterfaceJS::post_draw_viewport(RID p_render_target, c
|
||||
|
||||
GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
|
||||
|
||||
// @todo Support multiple eyes!
|
||||
godot_webxr_commit_for_eye(1, rt->fbo);
|
||||
godot_webxr_commit(rt->color);
|
||||
|
||||
return blit_to_screen;
|
||||
};
|
||||
|
@ -35,6 +35,7 @@ sys_env.AddJSLibraries(
|
||||
"js/libs/library_godot_os.js",
|
||||
"js/libs/library_godot_runtime.js",
|
||||
"js/libs/library_godot_input.js",
|
||||
"js/libs/library_godot_webgl2.js",
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -773,6 +773,9 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode
|
||||
if (emscripten_webgl_make_context_current(webgl_ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
|
||||
webgl2_init_failed = true;
|
||||
} else {
|
||||
if (!emscripten_webgl_enable_extension(webgl_ctx, "OVR_multiview2")) {
|
||||
// @todo Should we log this?
|
||||
}
|
||||
RasterizerGLES3::make_current();
|
||||
}
|
||||
}
|
||||
|
@ -34,4 +34,21 @@
|
||||
#include "GLES3/gl3.h"
|
||||
#include "webgl/webgl2.h"
|
||||
|
||||
#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630
|
||||
#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632
|
||||
#define GL_MAX_VIEWS_OVR 0x9631
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void godot_webgl2_glFramebufferTextureMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews);
|
||||
|
||||
#define glFramebufferTextureMultiviewOVR godot_webgl2_glFramebufferTextureMultiviewOVR
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // GODOT_WEBGL2_H
|
||||
|
52
platform/web/js/libs/library_godot_webgl2.js
Normal file
52
platform/web/js/libs/library_godot_webgl2.js
Normal file
@ -0,0 +1,52 @@
|
||||
/*************************************************************************/
|
||||
/* library_godot_webgl2.js */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
const GodotWebGL2 = {
|
||||
$GodotWebGL2__deps: ['$GL', '$GodotRuntime'],
|
||||
$GodotWebGL2: {},
|
||||
|
||||
godot_webgl2_glFramebufferTextureMultiviewOVR__deps: ['emscripten_webgl_get_current_context'],
|
||||
godot_webgl2_glFramebufferTextureMultiviewOVR__proxy: 'sync',
|
||||
godot_webgl2_glFramebufferTextureMultiviewOVR__sig: 'viiiiii',
|
||||
godot_webgl2_glFramebufferTextureMultiviewOVR: function (target, attachment, texture, level, base_view_index, num_views) {
|
||||
const context = GL.currentContext;
|
||||
if (typeof context.multiviewExt === 'undefined') {
|
||||
const ext = context.GLctx.getExtension('OVR_multiview2');
|
||||
if (!ext) {
|
||||
console.error('Trying to call glFramebufferTextureMultiviewOVR() without the OVR_multiview2 extension');
|
||||
return;
|
||||
}
|
||||
context.multiviewExt = ext;
|
||||
}
|
||||
context.multiviewExt.framebufferTextureMultiviewOVR(target, attachment, GL.textures[texture], level, base_view_index, num_views);
|
||||
},
|
||||
};
|
||||
|
||||
autoAddDeps(GodotWebGL2, '$GodotWebGL2');
|
||||
mergeInto(LibraryManager.library, GodotWebGL2);
|
27
thirdparty/glad/KHR/khrplatform.h
vendored
27
thirdparty/glad/KHR/khrplatform.h
vendored
@ -153,6 +153,20 @@ typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
@ -235,14 +249,21 @@ typedef unsigned short int khronos_uint16_t;
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef _WIN64
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
20
thirdparty/glad/glad.c
vendored
20
thirdparty/glad/glad.c
vendored
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
OpenGL loader generated by glad 0.1.34 on Tue Nov 17 16:41:02 2020.
|
||||
OpenGL loader generated by glad 0.1.36 on Sun Sep 4 15:50:32 2022.
|
||||
|
||||
Language/Generator: C/C++
|
||||
Specification: gl
|
||||
@ -11,16 +11,18 @@
|
||||
GL_ARB_framebuffer_object,
|
||||
GL_EXT_framebuffer_blit,
|
||||
GL_EXT_framebuffer_multisample,
|
||||
GL_EXT_framebuffer_object
|
||||
GL_EXT_framebuffer_object,
|
||||
GL_OVR_multiview,
|
||||
GL_OVR_multiview2
|
||||
Loader: True
|
||||
Local files: False
|
||||
Omit khrplatform: False
|
||||
Reproducible: False
|
||||
|
||||
Commandline:
|
||||
--profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object"
|
||||
--profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_OVR_multiview,GL_OVR_multiview2"
|
||||
Online:
|
||||
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object
|
||||
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object&extensions=GL_OVR_multiview&extensions=GL_OVR_multiview2
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@ -1000,6 +1002,8 @@ int GLAD_GL_ARB_framebuffer_object = 0;
|
||||
int GLAD_GL_EXT_framebuffer_blit = 0;
|
||||
int GLAD_GL_EXT_framebuffer_multisample = 0;
|
||||
int GLAD_GL_EXT_framebuffer_object = 0;
|
||||
int GLAD_GL_OVR_multiview = 0;
|
||||
int GLAD_GL_OVR_multiview2 = 0;
|
||||
PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB = NULL;
|
||||
PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB = NULL;
|
||||
PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB = NULL;
|
||||
@ -1023,6 +1027,7 @@ PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glad_glFramebufferTexture3DEXT = NULL;
|
||||
PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glad_glFramebufferRenderbufferEXT = NULL;
|
||||
PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glad_glGetFramebufferAttachmentParameterivEXT = NULL;
|
||||
PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT = NULL;
|
||||
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glad_glFramebufferTextureMultiviewOVR = NULL;
|
||||
static void load_GL_VERSION_1_0(GLADloadproc load) {
|
||||
if(!GLAD_GL_VERSION_1_0) return;
|
||||
glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
|
||||
@ -1844,6 +1849,10 @@ static void load_GL_EXT_framebuffer_object(GLADloadproc load) {
|
||||
glad_glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)load("glGetFramebufferAttachmentParameterivEXT");
|
||||
glad_glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC)load("glGenerateMipmapEXT");
|
||||
}
|
||||
static void load_GL_OVR_multiview(GLADloadproc load) {
|
||||
if(!GLAD_GL_OVR_multiview) return;
|
||||
glad_glFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)load("glFramebufferTextureMultiviewOVR");
|
||||
}
|
||||
static int find_extensionsGL(void) {
|
||||
if (!get_exts()) return 0;
|
||||
GLAD_GL_ARB_debug_output = has_ext("GL_ARB_debug_output");
|
||||
@ -1851,6 +1860,8 @@ static int find_extensionsGL(void) {
|
||||
GLAD_GL_EXT_framebuffer_blit = has_ext("GL_EXT_framebuffer_blit");
|
||||
GLAD_GL_EXT_framebuffer_multisample = has_ext("GL_EXT_framebuffer_multisample");
|
||||
GLAD_GL_EXT_framebuffer_object = has_ext("GL_EXT_framebuffer_object");
|
||||
GLAD_GL_OVR_multiview = has_ext("GL_OVR_multiview");
|
||||
GLAD_GL_OVR_multiview2 = has_ext("GL_OVR_multiview2");
|
||||
free_exts();
|
||||
return 1;
|
||||
}
|
||||
@ -1934,6 +1945,7 @@ int gladLoadGLLoader(GLADloadproc load) {
|
||||
load_GL_EXT_framebuffer_blit(load);
|
||||
load_GL_EXT_framebuffer_multisample(load);
|
||||
load_GL_EXT_framebuffer_object(load);
|
||||
load_GL_OVR_multiview(load);
|
||||
return GLVersion.major != 0 || GLVersion.minor != 0;
|
||||
}
|
||||
|
||||
|
25
thirdparty/glad/glad/glad.h
vendored
25
thirdparty/glad/glad/glad.h
vendored
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
OpenGL loader generated by glad 0.1.34 on Tue Nov 17 16:41:02 2020.
|
||||
OpenGL loader generated by glad 0.1.36 on Sun Sep 4 15:50:32 2022.
|
||||
|
||||
Language/Generator: C/C++
|
||||
Specification: gl
|
||||
@ -11,16 +11,18 @@
|
||||
GL_ARB_framebuffer_object,
|
||||
GL_EXT_framebuffer_blit,
|
||||
GL_EXT_framebuffer_multisample,
|
||||
GL_EXT_framebuffer_object
|
||||
GL_EXT_framebuffer_object,
|
||||
GL_OVR_multiview,
|
||||
GL_OVR_multiview2
|
||||
Loader: True
|
||||
Local files: False
|
||||
Omit khrplatform: False
|
||||
Reproducible: False
|
||||
|
||||
Commandline:
|
||||
--profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object"
|
||||
--profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_OVR_multiview,GL_OVR_multiview2"
|
||||
Online:
|
||||
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object
|
||||
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object&extensions=GL_OVR_multiview&extensions=GL_OVR_multiview2
|
||||
*/
|
||||
|
||||
|
||||
@ -3687,6 +3689,10 @@ GLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv;
|
||||
#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53
|
||||
#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54
|
||||
#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55
|
||||
#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630
|
||||
#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632
|
||||
#define GL_MAX_VIEWS_OVR 0x9631
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633
|
||||
#ifndef GL_ARB_debug_output
|
||||
#define GL_ARB_debug_output 1
|
||||
GLAPI int GLAD_GL_ARB_debug_output;
|
||||
@ -3776,6 +3782,17 @@ typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC)(GLenum target);
|
||||
GLAPI PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT;
|
||||
#define glGenerateMipmapEXT glad_glGenerateMipmapEXT
|
||||
#endif
|
||||
#ifndef GL_OVR_multiview
|
||||
#define GL_OVR_multiview 1
|
||||
GLAPI int GLAD_GL_OVR_multiview;
|
||||
typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews);
|
||||
GLAPI PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glad_glFramebufferTextureMultiviewOVR;
|
||||
#define glFramebufferTextureMultiviewOVR glad_glFramebufferTextureMultiviewOVR
|
||||
#endif
|
||||
#ifndef GL_OVR_multiview2
|
||||
#define GL_OVR_multiview2 1
|
||||
GLAPI int GLAD_GL_OVR_multiview2;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user