299 lines
8.8 KiB
C++
299 lines
8.8 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include <cstddef>
|
|
#include "gpu/gpu_renderer.h"
|
|
#include "gpu/gpu_geometry.h"
|
|
#include "core/renderer_environment_interface.h"
|
|
#include "core/terrain.h"
|
|
#include "core/geometry.h"
|
|
#include "sort/sort.h"
|
|
#include "container/container_sort.h"
|
|
#include "platform_config.h"
|
|
#include "log/log.h"
|
|
|
|
using namespace GS;
|
|
using namespace GPU;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Renderer::DrawList(const Stack <RenderPrimitive *> &dl_list, const DrawContext &dc)
|
|
{
|
|
ResetDisplayListCache();
|
|
DecayTerrainCache();
|
|
|
|
m_previous_iview = view_item->GetPreviousMatrix().InversedFast();
|
|
|
|
for (uint n = 0; n < dl_list.GetCount(); ++n)
|
|
{
|
|
RenderPrimitive *dl = dl_list[n];
|
|
|
|
if (!dl->item)
|
|
continue;
|
|
|
|
switch (dl->type)
|
|
{
|
|
case RenderPrimitive::TypeDisplayList:
|
|
if (dl->dlst)
|
|
{
|
|
m_previous_world = dl->item->GetPreviousMatrix();
|
|
SetWorldMatrix(dl->item->GetMatrix(), &dl->item->GetInverseMatrix());
|
|
DrawDisplayListCached(*dl->dlst, *dl->item, dc);
|
|
}
|
|
break;
|
|
|
|
case RenderPrimitive::TypeTerrainPatch:
|
|
if (dl->patch)
|
|
RenderTerrainPatch(*dl->patch, *dl->item, dc);
|
|
break;
|
|
|
|
case RenderPrimitive::TypeEmitter:
|
|
break;
|
|
}
|
|
}
|
|
|
|
ResetDisplayListCache();
|
|
stats.queue_pass++;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
static inline uint GetRenderPrimitiveSortKey(RenderPrimitive *a)
|
|
{
|
|
// Watch nRenderMaterial and nGPUDisplayList size in bytes so that this key stays optimal.
|
|
return ((((uint)a->dlst->material.c_ptr() >> 8) & 0xffff) << 16) + (((uint)a->dlst >> 7) & 0xffff);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Renderer::SortDisplayList(AutoStack <RenderPrimitive *> &d_list) const
|
|
{
|
|
const uint count = d_list.GetCount();
|
|
if (count == 0)
|
|
return;
|
|
try {
|
|
// Byte sort list.
|
|
typedef Sort<uint, RenderPrimitive *> SortPrimitive;
|
|
Array <SortPrimitive::Entry> sort_a(count), sort_b(count);
|
|
|
|
for (uint n = 0; n < count; ++n)
|
|
{
|
|
sort_a[n].o = d_list[n];
|
|
sort_a[n].v = GetRenderPrimitiveSortKey(d_list[n]);
|
|
}
|
|
Array <SortPrimitive::Entry> *out = SortPrimitive::ByteSort(count, &sort_a, &sort_b);
|
|
|
|
// Empty the source stack, prevent primitives cleanup...
|
|
d_list.DropContentOwnership();
|
|
|
|
// ...as we now insert them back in sorted order.
|
|
for (uint n = 0; n < count; ++n)
|
|
d_list.Push((*out)[n].o);
|
|
}
|
|
catch(char *e)
|
|
{
|
|
__LOG__ << "Failed to SortDisplayList.\n";
|
|
}
|
|
}
|
|
static int GetMaterialDrawPassIndex(Render::Material *m, float opacity = 1.f)
|
|
{
|
|
if (m->renderword & Core::Material::Render_UseFramebuffer)
|
|
return 2;
|
|
if ((m->blendop != Core::Material::Blend_None) || (opacity < 1.0))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
void Renderer::BuildDisplayLists(const AutoStack <Render::Primitive *> &p_list, AutoStack <RenderPrimitive *> *d_list) const
|
|
{
|
|
for (uint n = 0; n < p_list.GetCount(); ++n)
|
|
{
|
|
Render::Primitive *p = p_list[n];
|
|
|
|
switch (p->type)
|
|
{
|
|
case Render::Primitive::Type_Geometry:
|
|
if (Geometry *geo = (Geometry *)p->geometry.c_ptr())
|
|
for (uint n = 0; n < geo->display_list.GetCount(); ++n)
|
|
{
|
|
DisplayList *dlist = geo->display_list[n];
|
|
if (dlist->material.IsNull())
|
|
continue;
|
|
|
|
int draw_pass_index = GetMaterialDrawPassIndex(dlist->material, p->item->opacity);
|
|
d_list[draw_pass_index].Push(new RenderPrimitive(p->item, dlist));
|
|
}
|
|
break;
|
|
|
|
case Render::Primitive::Type_TerrainPatch:
|
|
if (p->patch->terrain->render_data->material.IsNull())
|
|
continue;
|
|
d_list[GetMaterialDrawPassIndex(p->patch->terrain->render_data->material, p->patch->terrain->opacity)].Push(new RenderPrimitive(p->item, p->patch));
|
|
break;
|
|
|
|
case Render::Primitive::Type_Emitter:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int n = 0; n < 3; ++n)
|
|
SortDisplayList(d_list[n]);
|
|
}
|
|
void Renderer::RenderList()
|
|
{
|
|
ScopedBenchmark bench(stats.bench_render);
|
|
PerfSetMarker("RenderList", Color::Green);
|
|
|
|
// Build primitive list.
|
|
stats.renderable_processed += BuildRenderablePrimitiveList(*view_item, *view_item, frustum_rlist.primitive_list);
|
|
stats.renderable_drawn += frustum_rlist.primitive_list.GetCount();
|
|
|
|
// Build display lists.
|
|
BuildDisplayLists(frustum_rlist.primitive_list, frustum_rlist.display_lists);
|
|
|
|
// Render opaque display lists.
|
|
switch (render_technique)
|
|
{
|
|
case TechniqueDeferred:
|
|
RenderListDeferred(frustum_rlist.display_lists[0]);
|
|
break;
|
|
|
|
case TechniqueForward:
|
|
RenderListForward(frustum_rlist.display_lists[0], DrawContext::Opaque);
|
|
|
|
if (environment_interface) // draw skybox
|
|
{
|
|
Render::Shader *user_skybox_shader = environment_interface->GetSkyboxShader();
|
|
|
|
Render::sTexture skybox_layers[2];
|
|
if (environment_interface->GetSkyboxLayers(skybox_layers) || user_skybox_shader)
|
|
DrawSkybox(skybox_layers, user_skybox_shader);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Render transparent display lists.
|
|
RenderListForward(frustum_rlist.display_lists[1], DrawContext::Alpha);
|
|
|
|
// Render frame buffer dependent display lists.
|
|
if (gpu_config.use_rtt && ((frustum_rlist.display_lists[2].GetCount() > 0) || registry.GetBool("FrameBufferAsTexture;", false)))
|
|
{
|
|
GrabDisplay(t_fx[0]);
|
|
RenderListForward(frustum_rlist.display_lists[2], DrawContext::Opaque);
|
|
}
|
|
|
|
// Render direct user primitives hook.
|
|
if (environment_interface)
|
|
environment_interface->OnRenderUser(this);
|
|
|
|
//
|
|
switch (render_technique)
|
|
{
|
|
case TechniqueDeferred:
|
|
// Render fog.
|
|
if (environment_interface)
|
|
if (environment_interface->IsFogEnabled() && !performance_tools.disable_fog)
|
|
{
|
|
EnableBlending(true);
|
|
RenderFullscreenQuad(*ds_fog_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y);
|
|
EnableBlending(false);
|
|
}
|
|
break;
|
|
|
|
case TechniqueForward:
|
|
// Resolve MSSA target.
|
|
if (gpu_config.enable_aa)
|
|
render_fbo->Blit(resolve_fbo, iRect(0, 0, dimensions.x, dimensions.y), iRect(0, 0, dimensions.x, dimensions.y), true, true);
|
|
break;
|
|
}
|
|
|
|
// Post-processes.
|
|
if (gpu_config.use_rtt)
|
|
{
|
|
t_final = ApplyPostProcessChain(frustum_rlist.display_lists);
|
|
resolve_fbo->SetColorTexture(t_final);
|
|
}
|
|
|
|
// Drop render list.
|
|
frustum_rlist.Clear(false);
|
|
|
|
// Subsequent draws should render to the final texture.
|
|
if (gpu_config.use_rtt)
|
|
SetCurrentFBO(resolve_fbo);
|
|
render_fbo = resolve_fbo;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Renderer::BeginDrawList()
|
|
{
|
|
PerfSetMarker("BeginDrawList", Color::Green);
|
|
|
|
// Cache per-frame values for coherency.
|
|
frame_clock = environment_interface ? environment_interface->GetClock() : 0;
|
|
pcf_radius = registry.GetReal("ShadowMapping:PCF:Radius", 1.75f);
|
|
|
|
if (environment_interface)
|
|
if (Core::Camera *c = environment_interface->GetCurrentCamera())
|
|
{
|
|
SetCamera(c);
|
|
ApplyCamera();
|
|
}
|
|
|
|
/*
|
|
MSAA needs a special resolve step so we work on a different FBO which
|
|
will later be resolved to the resolve FBO.
|
|
*/
|
|
render_fbo = gpu_config.enable_aa ? buffer_fbo : resolve_fbo;
|
|
|
|
if (render_technique == TechniqueForward)
|
|
{
|
|
if (gpu_config.use_rtt)
|
|
SetCurrentFBO(render_fbo);
|
|
|
|
// Clear output, if a skybox is set it will be rendered after the opaque pass.
|
|
if (environment_interface)
|
|
{
|
|
Render::Shader *user_skybox_shader = environment_interface->GetSkyboxShader();
|
|
|
|
Render::sTexture skybox_layers[2];
|
|
if (environment_interface->GetSkyboxLayers(skybox_layers) || user_skybox_shader)
|
|
Clear(0, 0, 0, 1, 1, ClearDepth);
|
|
else
|
|
{
|
|
Color bg = environment_interface->GetClearColor();
|
|
Clear(bg.x, bg.y, bg.z);
|
|
}
|
|
}
|
|
}
|
|
|
|
t_final = t_compose[0];
|
|
return true;
|
|
}
|
|
void Renderer::EndDrawList()
|
|
{
|
|
PerfSetMarker("EndDrawList", Color::Green);
|
|
|
|
// when entering this function the current FBO is expected to be render_FBO.
|
|
if (gpu_config.use_rtt)
|
|
{
|
|
// Blit result to user output_fbo.
|
|
if (output_fbo)
|
|
render_fbo->Blit(output_fbo, viewport.AsInt(), output_fbo_rect, true, false);
|
|
|
|
// Blit to frame buffer.
|
|
SetCurrentFBO(NULL);
|
|
fRect src(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y);
|
|
RenderFullscreenQuad(*single_texture_program, src, fRect(0, 0, 1, 1), t_final); // viewport is already set for destination so use the full rect output
|
|
|
|
// Restore default output.
|
|
resolve_fbo->SetColorTexture(t_final = t_compose[0]);
|
|
}
|
|
|
|
SetIndexSource(NULL);
|
|
SetVertexSource(NULL, 0);
|
|
}
|
|
//------------------------------------------------------------------------------
|