Files

749 lines
24 KiB
C++

/*
Copyright 2018 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "gl4.h"
#include "rend/gles/glcache.h"
#include "rend/gles/naomi2.h"
#include <memory>
static gl4PipelineShader* CurrentShader;
static GLuint stencilTexId;
static GLuint opaqueTexId[2];
static GLuint depthTexId;
GLuint texSamplers[2];
std::unique_ptr<OITFramebuffer> framebuffers[2];
static gl4PipelineShader *gl4GetProgram(bool cp_AlphaTest, bool pp_InsideClipping,
bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr, bool pp_Offset,
u32 pp_FogCtrl, bool pp_TwoVolumes, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping,
int palette, bool naomi2, Pass pass, bool secAccum)
{
u32 rv=0;
rv |= (int)pp_InsideClipping;
rv <<= 1; rv |= (int)cp_AlphaTest;
rv <<= 1; rv |= (int)pp_Texture;
rv <<= 1; rv |= (int)pp_UseAlpha;
rv <<= 1; rv |= (int)pp_IgnoreTexA;
rv <<= 2; rv |= pp_ShadInstr;
rv <<= 1; rv |= (int)pp_Offset;
rv <<= 2; rv |= pp_FogCtrl;
rv <<= 1; rv |= (int)pp_TwoVolumes;
rv <<= 1; rv |= (int)pp_Gouraud;
rv <<= 1; rv |= (int)pp_BumpMap;
rv <<= 1; rv |= (int)fog_clamping;
rv <<= 2; rv |= palette;
rv <<= 1; rv |= (int)naomi2;
rv <<= 2; rv |= (int)pass;
rv <<= 1; rv |= (int)(!settings.platform.isNaomi2() && config::NativeDepthInterpolation);
rv <<= 1; rv |= (int)secAccum;
gl4PipelineShader *shader = &gl4.shaders[rv];
if (shader->program == 0)
{
shader->cp_AlphaTest = cp_AlphaTest;
shader->pp_InsideClipping = pp_InsideClipping;
shader->pp_Texture = pp_Texture;
shader->pp_UseAlpha = pp_UseAlpha;
shader->pp_IgnoreTexA = pp_IgnoreTexA;
shader->pp_ShadInstr = pp_ShadInstr;
shader->pp_Offset = pp_Offset;
shader->pp_FogCtrl = pp_FogCtrl;
shader->pp_TwoVolumes = pp_TwoVolumes;
shader->pp_Gouraud = pp_Gouraud;
shader->pp_BumpMap = pp_BumpMap;
shader->fog_clamping = fog_clamping;
shader->palette = palette;
shader->naomi2 = naomi2;
shader->pass = pass;
shader->divPosZ = !settings.platform.isNaomi2() && config::NativeDepthInterpolation;
shader->secAccum = secAccum;
gl4CompilePipelineShader(shader);
}
return shader;
}
static void setTextureRepeatMode(int index, GLuint dir, u32 clamp, u32 mirror)
{
if (clamp)
glSamplerParameteri(texSamplers[index], dir, GL_CLAMP_TO_EDGE);
else
glSamplerParameteri(texSamplers[index], dir, mirror ? GL_MIRRORED_REPEAT : GL_REPEAT);
}
static void setBaseClipping()
{
if (gl4ShaderUniforms.base_clipping.enabled)
{
glcache.Enable(GL_SCISSOR_TEST);
glcache.Scissor(gl4ShaderUniforms.base_clipping.x, gl4ShaderUniforms.base_clipping.y, gl4ShaderUniforms.base_clipping.width, gl4ShaderUniforms.base_clipping.height);
}
else
glcache.Disable(GL_SCISSOR_TEST);
}
template <u32 Type, bool SortingEnabled, Pass pass>
void OpenGL4Renderer::setGPState(const PolyParam *gp)
{
// Trilinear filtering. Ignore if texture isn't mipmapped (shenmue snowflakes)
if (gp->pcw.Texture && gp->tsp.FilterMode > 1 && Type != ListType_Punch_Through && gp->tcw.MipMapped == 1)
{
gl4ShaderUniforms.trilinear_alpha = 0.25f * (gp->tsp.MipMapD & 0x3);
if (gp->tsp.FilterMode == 2)
// Trilinear pass A
gl4ShaderUniforms.trilinear_alpha = 1.0f - gl4ShaderUniforms.trilinear_alpha;
}
else
gl4ShaderUniforms.trilinear_alpha = 1.0;
Rect clip_rect;
TileClipping clipmode = gl.matrices.getTileClip(gp->tileclip, clip_rect);
int gpuPalette = gp->texture == nullptr || !gp->texture->gpuPalette ? 0
: gp->tsp.FilterMode + 1;
if (gpuPalette != 0)
{
if (config::TextureFiltering == 1)
gpuPalette = 1; // force nearest
else if (config::TextureFiltering == 2)
gpuPalette = 2; // force linear
}
if constexpr (pass == Pass::Color && Type == ListType_Translucent && !SortingEnabled)
{
if (gp->tsp.DstSelect == 1)
{
if (!renderingToSecAccum)
{
renderingToSecAccum = true;
glActiveTexture(GL_TEXTURE7);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
getSecondaryAccum()->bind();
}
}
else if (renderingToSecAccum)
{
renderingToSecAccum = false;
getPrimaryFB()->bind();
glActiveTexture(GL_TEXTURE7);
glBindTexture(GL_TEXTURE_2D, getSecondaryAccum()->getTexture());
glActiveTexture(GL_TEXTURE0);
}
}
if (pass == Pass::Depth)
{
if (Type != ListType_Punch_Through)
gpuPalette = 0;
CurrentShader = gl4GetProgram(Type == ListType_Punch_Through ? true : false,
clipmode == TileClipping::Inside,
Type == ListType_Punch_Through ? gp->pcw.Texture : false,
true,
gp->tsp.IgnoreTexA,
0,
false,
2,
false, // TODO Can PT have two different textures for area 0 and 1 ??
false,
false,
false,
gpuPalette,
gp->isNaomi2(),
pass,
false);
}
else
{
// Two volumes mode only supported for OP and PT
bool two_volumes_mode = (gp->tsp1.full != (u32)-1) && Type != ListType_Translucent;
bool color_clamp = gp->tsp.ColorClamp && (gl.rendContext->fog_clamp_min.full != 0 || gl.rendContext->fog_clamp_max.full != 0xffffffff);
int fog_ctrl = config::Fog ? gp->tsp.FogCtrl : 2;
CurrentShader = gl4GetProgram(Type == ListType_Punch_Through ? true : false,
clipmode == TileClipping::Inside,
gp->pcw.Texture,
gp->tsp.UseAlpha,
gp->tsp.IgnoreTexA,
gp->tsp.ShadInstr,
gp->pcw.Offset,
fog_ctrl,
two_volumes_mode,
gp->pcw.Gouraud,
gp->tcw.PixelFmt == PixelBumpMap,
color_clamp,
gpuPalette,
gp->isNaomi2(),
pass,
Type == ListType_Translucent && !SortingEnabled && gp->tsp.SrcSelect == 1);
}
glcache.UseProgram(CurrentShader->program);
if (gpuPalette != 0)
{
if (gp->tcw.PixelFmt == PixelPal4)
gl4ShaderUniforms.palette_index = gp->tcw.PalSelect << 4;
else
gl4ShaderUniforms.palette_index = (gp->tcw.PalSelect >> 4) << 8;
}
gl4ShaderUniforms.tsp0 = gp->tsp;
gl4ShaderUniforms.tsp1 = gp->tsp1;
gl4ShaderUniforms.tcw0 = gp->tcw;
gl4ShaderUniforms.tcw1 = gp->tcw1;
gl4ShaderUniforms.Set(CurrentShader);
if (pass == Pass::Color) {
glcache.Enable(GL_BLEND);
glcache.BlendFunc(srcBlendToGL(gp->tsp.SrcInstr), dstBlendToGL(gp->tsp.DstInstr));
}
else {
glcache.Disable(GL_BLEND);
}
if (clipmode == TileClipping::Inside)
glUniform4f(CurrentShader->pp_ClipTest, (float)clip_rect.origin.x, (float)clip_rect.origin.y,
(float)clip_rect.bottomRight().x, (float)clip_rect.bottomRight().y);
if (clipmode == TileClipping::Outside) {
glcache.Enable(GL_SCISSOR_TEST);
glcache.Scissor(clip_rect.origin.x, clip_rect.origin.y, clip_rect.size.x, clip_rect.size.y);
}
else {
setBaseClipping();
}
// This bit controls which pixels are affected by modvols
const u32 stencil = gp->pcw.Shadow != 0 ? 0x80 : 0x0;
glcache.StencilFunc(GL_ALWAYS, stencil, stencil);
if (CurrentShader->pp_Texture)
{
for (int i = 0; i < 2; i++)
{
glActiveTexture(GL_TEXTURE0 + i);
TextureCacheData *texture = (TextureCacheData *)(i == 0 ? gp->texture : gp->texture1);
if (i == 0)
glcache.BindTexture(GL_TEXTURE_2D, texture == nullptr ? 0 : texture->texID);
else
glBindTexture(GL_TEXTURE_2D, texture == nullptr ? 0 : texture->texID);
if (texture != nullptr)
{
TSP tsp = i == 0 ? gp->tsp : gp->tsp1;
glBindSampler(i, texSamplers[i]);
setTextureRepeatMode(i, GL_TEXTURE_WRAP_S, tsp.ClampU, tsp.FlipU);
setTextureRepeatMode(i, GL_TEXTURE_WRAP_T, tsp.ClampV, tsp.FlipV);
bool nearest_filter;
if (gpuPalette != 0)
nearest_filter = true;
else if (config::TextureFiltering == 0)
nearest_filter = tsp.FilterMode == 0;
else if (config::TextureFiltering == 1)
nearest_filter = true;
else
nearest_filter = false;
bool mipmapped = gp->tcw.MipMapped != 0 && gp->tcw.ScanOrder == 0 && config::UseMipmaps;
//set texture filter mode
if (nearest_filter)
{
//nearest-neighbor filtering
glSamplerParameteri(texSamplers[i], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(texSamplers[i], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
else
{
//bilinear filtering
//PowerVR supports also trilinear via two passes, but we ignore that for now
glSamplerParameteri(texSamplers[i], GL_TEXTURE_MIN_FILTER, mipmapped && Type != ListType_Punch_Through ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
glSamplerParameteri(texSamplers[i], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
#ifdef GL_TEXTURE_LOD_BIAS
if (mipmapped && !gl.is_gles)
glSamplerParameterf(texSamplers[i], GL_TEXTURE_LOD_BIAS, D_Adjust_LoD_Bias[tsp.MipMapD]);
#endif
if (gl.max_anisotropy > 1.f)
{
if (config::AnisotropicFiltering > 1 && !nearest_filter)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY,
std::min<float>(config::AnisotropicFiltering, gl.max_anisotropy));
else
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 1.f);
}
}
}
glActiveTexture(GL_TEXTURE0);
}
setCull(gp->isp.CullMode ^ 1);
//set Z mode, only if required
if (Type == ListType_Punch_Through || (pass == Pass::Depth && SortingEnabled))
{
glcache.DepthFunc(GL_GEQUAL);
}
else if (Type == ListType_Opaque || (Type == ListType_Translucent && !SortingEnabled))
{
glcache.DepthFunc(depthModeToGL(gp->isp.DepthMode));
}
if (pass == Pass::Depth || pass == Pass::Color)
{
// Z Write Disable seems to be ignored for punch-through polys
// Fixes Worms World Party, Bust-a-Move 4 and Re-Volt
if (Type == ListType_Punch_Through)
glcache.DepthMask(GL_TRUE);
else
glcache.DepthMask(!gp->isp.ZWriteDis);
}
else {
glcache.DepthMask(GL_FALSE);
}
if (gp->isNaomi2())
setN2Uniforms(gp, CurrentShader, *gl.rendContext);
}
template <u32 Type, bool SortingEnabled, Pass pass>
void OpenGL4Renderer::drawList(const std::vector<PolyParam>& gply, int first, int count)
{
if (count == 0)
return;
const PolyParam* params = &gply[first];
u32 firstVertexIdx = Type == ListType_Translucent ? gl.rendContext->idx[gply[0].first] : 0;
while (count-- > 0)
{
if (params->count > 2)
{
if ((Type == ListType_Opaque || (Type == ListType_Translucent && !SortingEnabled)) && params->isp.DepthMode == 0)
{
// depthFunc = never
params++;
continue;
}
if (pass == Pass::OIT && Type == ListType_Translucent && params->tsp.SrcInstr == 0 && params->tsp.DstInstr == 1)
{
// dst = dst
params++;
continue;
}
gl4ShaderUniforms.poly_number = ((params - &gply[0]) << 17) - firstVertexIdx;
setGPState<Type, SortingEnabled, pass>(params);
glDrawElements(GL_TRIANGLE_STRIP, params->count, GL_UNSIGNED_INT, (GLvoid*)(sizeof(u32) * params->first)); glCheck();
}
params++;
}
}
void gl4SetupMainVBO()
{
gl4.vbo.getMainVAO().bind(gl4.vbo.getVertexBuffer(), gl4.vbo.getIndexBuffer());
}
void Gl4MainVertexArray::defineVtxAttribs()
{
//setup vertex buffers attrib pointers
glEnableVertexAttribArray(VERTEX_POS_ARRAY);
glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,x));
glEnableVertexAttribArray(VERTEX_COL_BASE_ARRAY);
glVertexAttribPointer(VERTEX_COL_BASE_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex,col));
glEnableVertexAttribArray(VERTEX_COL_OFFS_ARRAY);
glVertexAttribPointer(VERTEX_COL_OFFS_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex,spc));
glEnableVertexAttribArray(VERTEX_UV_ARRAY);
glVertexAttribPointer(VERTEX_UV_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,u));
glEnableVertexAttribArray(VERTEX_COL_BASE1_ARRAY);
glVertexAttribPointer(VERTEX_COL_BASE1_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, col1));
glEnableVertexAttribArray(VERTEX_COL_OFFS1_ARRAY);
glVertexAttribPointer(VERTEX_COL_OFFS1_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, spc1));
glEnableVertexAttribArray(VERTEX_UV1_ARRAY);
glVertexAttribPointer(VERTEX_UV1_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, u1));
glEnableVertexAttribArray(VERTEX_NORM_ARRAY);
glVertexAttribPointer(VERTEX_NORM_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, nx));
}
void gl4SetupModvolVBO()
{
gl4.vbo.getModVolVAO().bind(gl4.vbo.getModVolBuffer());
}
void Gl4ModvolVertexArray::defineVtxAttribs()
{
glEnableVertexAttribArray(VERTEX_POS_ARRAY); glCheck();
glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(float)*3, (void*)0); glCheck();
}
void OpenGL4Renderer::drawModVols(int first, int count)
{
if (count == 0 || gl.rendContext->modtrig.empty())
return;
gl4SetupModvolVBO();
glcache.Disable(GL_BLEND);
setBaseClipping();
glcache.Enable(GL_DEPTH_TEST);
glcache.DepthMask(GL_FALSE);
glcache.DepthFunc(GL_GREATER);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
ModifierVolumeParam* params = &gl.rendContext->global_param_mvo[first];
int mod_base = -1;
for (int cmv = 0; cmv < count; cmv++)
{
ModifierVolumeParam& param = params[cmv];
if (param.count == 0)
continue;
if (param.isNaomi2())
{
glcache.UseProgram(gl4.n2ModVolShader.program);
glUniformMatrix4fv(gl4.n2ModVolShader.mvMat, 1, GL_FALSE, gl.rendContext->matrices[param.mvMatrix].mat);
glUniformMatrix4fv(gl4.n2ModVolShader.projMat, 1, GL_FALSE, gl.rendContext->matrices[param.projMatrix].mat);
}
else
glcache.UseProgram(gl4.modvol_shader.program);
u32 mv_mode = param.isp.DepthMode;
if (mod_base == -1)
mod_base = param.first;
if (!param.isp.VolumeLast && mv_mode > 0)
setModVolMode(Or, param.isp); // OR'ing (open volume or quad)
else
setModVolMode(Xor, param.isp); // XOR'ing (closed volume)
glDrawArrays(GL_TRIANGLES, param.first * 3, param.count * 3);
if (mv_mode == 1 || mv_mode == 2)
{
// Sum the area
setModVolMode(mv_mode == 1 ? Inclusion : Exclusion, param.isp);
glDrawArrays(GL_TRIANGLES, mod_base * 3, (param.first + param.count - mod_base) * 3);
mod_base = -1;
}
}
//restore states
gl4SetupMainVBO();
glcache.Enable(GL_DEPTH_TEST);
glcache.DepthMask(GL_TRUE);
}
static GLuint createColorFBOTexture(int width, int height)
{
GLuint texId = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, texId);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glCheck();
return texId;
}
OITFramebuffer::OITFramebuffer(int width, int height, GLuint texId, GLuint depth, GLuint stencil)
{
this->width = width;
this->height = height;
this->texture = texId;
// Create the framebuffer
glGenFramebuffers(1, &this->framebuffer);
bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0);
if (depth != 0)
this->depthBuffer = depth;
else if (stencil != 0)
this->depthBuffer = stencil;
glcache.BindTexture(GL_TEXTURE_2D, this->depthBuffer); glCheck();
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glcache.TexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, depth != 0 ? GL_DEPTH_COMPONENT : GL_STENCIL_INDEX);
// with GL_DEPTH32F_STENCIL8 stencil doesn't work on Mali. GL_DEPTH24_STENCIL8 works but moire effect on transparent texs (nvidia)
glTexStorage2D(GL_TEXTURE_2D, 1, gl.mali ? GL_DEPTH24_STENCIL8 : GL_DEPTH32F_STENCIL8, width, height);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, this->depthBuffer, 0);
glCheck();
GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (uStatus != GL_FRAMEBUFFER_COMPLETE) {
ERROR_LOG(RENDERER, "Framebuffer creation failed: %x", uStatus);
throw RendererException("OpenGL framebuffer creation failed");
}
}
void gl4CreateTextures(int width, int height)
{
stencilTexId = glcache.GenTexture();
depthTexId = glcache.GenTexture();
for (int i = 0; i < 2; i++) {
opaqueTexId[i] = createColorFBOTexture(width, height);
framebuffers[i] = std::make_unique<OITFramebuffer>(width, height, opaqueTexId[i], i == 0 ? 0 : depthTexId, i == 1 ? 0 : stencilTexId);
}
}
void OpenGL4Renderer::drawStrips(int width, int height)
{
checkOverflowAndReset();
if (!gl.rendContext->isRTT)
{
glcache.Disable(GL_SCISSOR_TEST);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
if (gl.rendContext->clearFramebuffer)
{
// Clear framebuffer
framebuffers[1]->bind();
glcache.ClearColor(VO_BORDER_COL.red(), VO_BORDER_COL.green(), VO_BORDER_COL.blue(), 1.f);
glClear(GL_COLOR_BUFFER_BIT);
}
else
{
// Copy previous framebuffer content (in case of partial render)
framebuffers[1]->bind(GL_DRAW_FRAMEBUFFER);
outputFB->bind(GL_READ_FRAMEBUFFER);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glCheck();
}
if (gl4ShaderUniforms.base_clipping.enabled)
glcache.Enable(GL_SCISSOR_TEST);
}
if (texSamplers[0] == 0)
glGenSamplers(2, texSamplers);
framebuffers[0]->bind();
glcache.DepthMask(GL_TRUE);
glClearDepthf(0.f);
glcache.StencilMask(0xFF);
glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glCheck();
//Draw the strips !
//We use sampler 0
glActiveTexture(GL_TEXTURE0);
#ifdef GL_LAST_VERTEX_CONVENTION
if (!gl.is_gles)
glProvokingVertex(GL_LAST_VERTEX_CONVENTION);
#endif
renderingToSecAccum = false;
RenderPass previous_pass = {};
int render_pass_count = gl.rendContext->render_passes.size();
for (int render_pass = 0; render_pass < render_pass_count; render_pass++)
{
const RenderPass& current_pass = gl.rendContext->render_passes[render_pass];
// Check if we can skip this pass, in part or completely, in case nothing is drawn (Cosmic Smash)
bool skip_op_pt = true;
bool skip_tr = true;
for (u32 j = previous_pass.op_count; skip_op_pt && j < current_pass.op_count; j++)
{
if (gl.rendContext->global_param_op[j].count > 2)
skip_op_pt = false;
}
for (u32 j = previous_pass.pt_count; skip_op_pt && j < current_pass.pt_count; j++)
{
if (gl.rendContext->global_param_pt[j].count > 2)
skip_op_pt = false;
}
for (u32 j = previous_pass.tr_count; skip_tr && j < current_pass.tr_count; j++)
{
if (gl.rendContext->global_param_tr[j].count > 2)
skip_tr = false;
}
if (skip_op_pt && skip_tr)
{
previous_pass = current_pass;
continue;
}
DEBUG_LOG(RENDERER, "Render pass %d/%d OP %d PT %d TR %d MV %d TMV %d autosort %d", render_pass + 1, render_pass_count,
current_pass.op_count - previous_pass.op_count,
current_pass.pt_count - previous_pass.pt_count,
current_pass.tr_count - previous_pass.tr_count,
current_pass.mvo_count - previous_pass.mvo_count,
current_pass.mv_op_tr_shared ? current_pass.mvo_count - previous_pass.mvo_count : current_pass.mvo_tr_count - previous_pass.mvo_tr_count,
current_pass.autosort);
gl4SetupMainVBO();
if (!skip_op_pt)
{
//
// PASS 1: Geometry pass to update depth and stencil
//
framebuffers[0]->bind();
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glcache.Enable(GL_DEPTH_TEST);
glcache.DepthMask(GL_TRUE);
glcache.Enable(GL_STENCIL_TEST);
glcache.StencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
drawList<ListType_Opaque, false, Pass::Depth>(gl.rendContext->global_param_op, previous_pass.op_count, current_pass.op_count - previous_pass.op_count);
drawList<ListType_Punch_Through, false, Pass::Depth>(gl.rendContext->global_param_pt, previous_pass.pt_count, current_pass.pt_count - previous_pass.pt_count);
// Modifier volumes
if (config::ModifierVolumes)
drawModVols(previous_pass.mvo_count, current_pass.mvo_count - previous_pass.mvo_count);
//
// PASS 2: Render OP and PT to fbo
//
framebuffers[1]->bind();
if (render_pass == 0)
{
glcache.DepthMask(GL_TRUE);
glClearDepthf(0.f);
glClear(GL_DEPTH_BUFFER_BIT);
}
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glcache.Disable(GL_STENCIL_TEST);
// Bind stencil buffer for the fragment shader (shadowing)
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, stencilTexId);
glActiveTexture(GL_TEXTURE0);
glCheck();
//Opaque
drawList<ListType_Opaque, false, Pass::Color>(gl.rendContext->global_param_op, previous_pass.op_count, current_pass.op_count - previous_pass.op_count);
//Alpha tested
drawList<ListType_Punch_Through, false, Pass::Color>(gl.rendContext->global_param_pt, previous_pass.pt_count, current_pass.pt_count - previous_pass.pt_count);
// Unbind stencil
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
}
if (!skip_tr)
{
//
// PASS 3: Render TR to a-buffers
//
if (current_pass.autosort)
{
framebuffers[0]->bind();
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glcache.Disable(GL_DEPTH_TEST);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, depthTexId);
glActiveTexture(GL_TEXTURE0);
drawList<ListType_Translucent, true, Pass::OIT>(gl.rendContext->global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count);
// Translucent modifier volumes
if (config::ModifierVolumes)
{
setBaseClipping();
if (current_pass.mv_op_tr_shared)
drawTranslucentModVols(previous_pass.mvo_count, current_pass.mvo_count - previous_pass.mvo_count, true);
else
drawTranslucentModVols(previous_pass.mvo_tr_count, current_pass.mvo_tr_count - previous_pass.mvo_tr_count, false);
}
if (render_pass < render_pass_count - 1)
{
//
// PASS 3b: Geometry pass with TR to update the depth for the next TA render pass
//
// Unbind depth texture
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
framebuffers[1]->bind();
glcache.Enable(GL_DEPTH_TEST);
drawList<ListType_Translucent, true, Pass::Depth>(gl.rendContext->global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count);
}
}
else
{
framebuffers[1]->bind();
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glcache.Enable(GL_DEPTH_TEST);
drawList<ListType_Translucent, false, Pass::Color>(gl.rendContext->global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count);
glcache.Disable(GL_BLEND);
}
glCheck();
if (render_pass < render_pass_count - 1)
{
//
// PASS 3c: Render a-buffer to other fbo texture then swap
//
framebuffers[0]->bind();
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glActiveTexture(GL_TEXTURE0);
glBindSampler(0, 0);
glcache.BindTexture(GL_TEXTURE_2D, opaqueTexId[1]);
renderABuffer(false);
glcache.BindTexture(GL_TEXTURE_2D, 0);
glCheck();
std::swap(opaqueTexId[0], opaqueTexId[1]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, opaqueTexId[0], 0);
framebuffers[1]->bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, opaqueTexId[1], 0);
}
}
if (!skip_op_pt && render_pass < render_pass_count - 1)
{
// Clear the stencil from this pass
framebuffers[0]->bind();
glcache.StencilMask(0xFF);
glClear(GL_STENCIL_BUFFER_BIT);
}
previous_pass = current_pass;
}
//
// PASS 4: Render a-buffers to screen
//
outputFB->bind(); glCheck();
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glActiveTexture(GL_TEXTURE0);
glBindSampler(0, 0);
glcache.BindTexture(GL_TEXTURE_2D, opaqueTexId[1]);
renderABuffer(true);
}