1201 lines
37 KiB
C++
1201 lines
37 KiB
C++
/**
|
|
* @file llglsandbox.cpp
|
|
* @brief GL functionality access
|
|
*
|
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
/**
|
|
* Contains ALL methods which directly access GL functionality
|
|
* except for core rendering engine functionality.
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llviewercontrol.h"
|
|
|
|
#include "llgl.h"
|
|
#include "llrender.h"
|
|
#include "llglheaders.h"
|
|
#include "llparcel.h"
|
|
#include "llui.h"
|
|
|
|
#include "lldrawable.h"
|
|
#include "lltextureentry.h"
|
|
#include "llviewercamera.h"
|
|
|
|
#include "llvoavatarself.h"
|
|
#include "llsky.h"
|
|
#include "llagent.h"
|
|
#include "lltoolmgr.h"
|
|
#include "llselectmgr.h"
|
|
#include "llhudmanager.h"
|
|
#include "llhudtext.h"
|
|
#include "llrendersphere.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "lltoolselectrect.h"
|
|
#include "llviewerwindow.h"
|
|
#include "llsurface.h"
|
|
#include "llwind.h"
|
|
#include "llworld.h"
|
|
#include "llviewerparcelmgr.h"
|
|
#include "llviewerregion.h"
|
|
#include "llpreviewtexture.h"
|
|
#include "llresmgr.h"
|
|
#include "pipeline.h"
|
|
#include "llspatialpartition.h"
|
|
#include "llviewershadermgr.h"
|
|
|
|
#include <vector>
|
|
|
|
// Height of the yellow selection highlight posts for land
|
|
const F32 PARCEL_POST_HEIGHT = 0.666f;
|
|
|
|
// Returns true if you got at least one object
|
|
void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask)
|
|
{
|
|
LLVector3 av_pos = gAgent.getPositionAgent();
|
|
F32 select_dist_squared = gSavedSettings.getF32("MaxSelectDistance");
|
|
select_dist_squared = select_dist_squared * select_dist_squared;
|
|
|
|
BOOL deselect = (mask == MASK_CONTROL);
|
|
S32 left = llmin(x, mDragStartX);
|
|
S32 right = llmax(x, mDragStartX);
|
|
S32 top = llmax(y, mDragStartY);
|
|
S32 bottom =llmin(y, mDragStartY);
|
|
|
|
left = ll_round((F32) left * LLUI::getScaleFactor().mV[VX]);
|
|
right = ll_round((F32) right * LLUI::getScaleFactor().mV[VX]);
|
|
top = ll_round((F32) top * LLUI::getScaleFactor().mV[VY]);
|
|
bottom = ll_round((F32) bottom * LLUI::getScaleFactor().mV[VY]);
|
|
|
|
F32 old_far_plane = LLViewerCamera::getInstance()->getFar();
|
|
F32 old_near_plane = LLViewerCamera::getInstance()->getNear();
|
|
|
|
S32 width = right - left + 1;
|
|
S32 height = top - bottom + 1;
|
|
|
|
BOOL grow_selection = FALSE;
|
|
BOOL shrink_selection = FALSE;
|
|
|
|
if (height > mDragLastHeight || width > mDragLastWidth)
|
|
{
|
|
grow_selection = TRUE;
|
|
}
|
|
if (height < mDragLastHeight || width < mDragLastWidth)
|
|
{
|
|
shrink_selection = TRUE;
|
|
}
|
|
|
|
if (!grow_selection && !shrink_selection)
|
|
{
|
|
// nothing to do
|
|
return;
|
|
}
|
|
|
|
mDragLastHeight = height;
|
|
mDragLastWidth = width;
|
|
|
|
S32 center_x = (left + right) / 2;
|
|
S32 center_y = (top + bottom) / 2;
|
|
|
|
// save drawing mode
|
|
gGL.matrixMode(LLRender::MM_PROJECTION);
|
|
gGL.pushMatrix();
|
|
|
|
BOOL limit_select_distance = gSavedSettings.getBOOL("LimitSelectDistance");
|
|
if (limit_select_distance)
|
|
{
|
|
// ...select distance from control
|
|
LLVector3 relative_av_pos = av_pos;
|
|
relative_av_pos -= LLViewerCamera::getInstance()->getOrigin();
|
|
|
|
F32 new_far = relative_av_pos * LLViewerCamera::getInstance()->getAtAxis() + gSavedSettings.getF32("MaxSelectDistance");
|
|
F32 new_near = relative_av_pos * LLViewerCamera::getInstance()->getAtAxis() - gSavedSettings.getF32("MaxSelectDistance");
|
|
|
|
new_near = llmax(new_near, 0.1f);
|
|
|
|
LLViewerCamera::getInstance()->setFar(new_far);
|
|
LLViewerCamera::getInstance()->setNear(new_near);
|
|
}
|
|
LLViewerCamera::getInstance()->setPerspective(FOR_SELECTION,
|
|
center_x-width/2, center_y-height/2, width, height,
|
|
limit_select_distance);
|
|
|
|
if (shrink_selection)
|
|
{
|
|
struct f : public LLSelectedObjectFunctor
|
|
{
|
|
virtual bool apply(LLViewerObject* vobjp)
|
|
{
|
|
LLDrawable* drawable = vobjp->mDrawable;
|
|
if (!drawable || vobjp->getPCode() != LL_PCODE_VOLUME || vobjp->isAttachment())
|
|
{
|
|
return true;
|
|
}
|
|
S32 result = LLViewerCamera::getInstance()->sphereInFrustum(drawable->getPositionAgent(), drawable->getRadius());
|
|
switch (result)
|
|
{
|
|
case 0:
|
|
LLSelectMgr::getInstance()->unhighlightObjectOnly(vobjp);
|
|
break;
|
|
case 1:
|
|
// check vertices
|
|
if (!LLViewerCamera::getInstance()->areVertsVisible(vobjp, LLSelectMgr::sRectSelectInclusive))
|
|
{
|
|
LLSelectMgr::getInstance()->unhighlightObjectOnly(vobjp);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
} func;
|
|
LLSelectMgr::getInstance()->getHighlightedObjects()->applyToObjects(&func);
|
|
}
|
|
|
|
if (grow_selection)
|
|
{
|
|
std::vector<LLDrawable*> potentials;
|
|
|
|
for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
|
|
iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
|
|
{
|
|
LLViewerRegion* region = *iter;
|
|
for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++)
|
|
{
|
|
LLSpatialPartition* part = region->getSpatialPartition(i);
|
|
if (part)
|
|
{
|
|
part->cull(*LLViewerCamera::getInstance(), &potentials, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (std::vector<LLDrawable*>::iterator iter = potentials.begin();
|
|
iter != potentials.end(); iter++)
|
|
{
|
|
LLDrawable* drawable = *iter;
|
|
LLViewerObject* vobjp = drawable->getVObj();
|
|
|
|
if (!drawable || !vobjp ||
|
|
vobjp->getPCode() != LL_PCODE_VOLUME ||
|
|
vobjp->isAttachment() ||
|
|
(deselect && !vobjp->isSelected()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (limit_select_distance && dist_vec_squared(drawable->getWorldPosition(), av_pos) > select_dist_squared)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
S32 result = LLViewerCamera::getInstance()->sphereInFrustum(drawable->getPositionAgent(), drawable->getRadius());
|
|
if (result)
|
|
{
|
|
switch (result)
|
|
{
|
|
case 1:
|
|
// check vertices
|
|
if (LLViewerCamera::getInstance()->areVertsVisible(vobjp, LLSelectMgr::sRectSelectInclusive))
|
|
{
|
|
LLSelectMgr::getInstance()->highlightObjectOnly(vobjp);
|
|
}
|
|
break;
|
|
case 2:
|
|
LLSelectMgr::getInstance()->highlightObjectOnly(vobjp);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// restore drawing mode
|
|
gGL.matrixMode(LLRender::MM_PROJECTION);
|
|
gGL.popMatrix();
|
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
|
|
|
// restore camera
|
|
LLViewerCamera::getInstance()->setFar(old_far_plane);
|
|
LLViewerCamera::getInstance()->setNear(old_near_plane);
|
|
gViewerWindow->setup3DRender();
|
|
}
|
|
|
|
const F32 WIND_RELATIVE_ALTITUDE = 25.f;
|
|
|
|
void LLWind::renderVectors()
|
|
{
|
|
// Renders the wind as vectors (used for debug)
|
|
S32 i,j;
|
|
F32 x,y;
|
|
|
|
F32 region_width_meters = LLWorld::getInstance()->getRegionWidthInMeters();
|
|
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.pushMatrix();
|
|
LLVector3 origin_agent;
|
|
origin_agent = gAgent.getPosAgentFromGlobal(mOriginGlobal);
|
|
gGL.translatef(origin_agent.mV[VX], origin_agent.mV[VY], gAgent.getPositionAgent().mV[VZ] + WIND_RELATIVE_ALTITUDE);
|
|
for (j = 0; j < mSize; j++)
|
|
{
|
|
for (i = 0; i < mSize; i++)
|
|
{
|
|
x = mVelX[i + j*mSize] * WIND_SCALE_HACK;
|
|
y = mVelY[i + j*mSize] * WIND_SCALE_HACK;
|
|
gGL.pushMatrix();
|
|
gGL.translatef((F32)i * region_width_meters/mSize, (F32)j * region_width_meters/mSize, 0.0);
|
|
gGL.color3f(0,1,0);
|
|
gGL.begin(LLRender::POINTS);
|
|
gGL.vertex3f(0,0,0);
|
|
gGL.end();
|
|
gGL.color3f(1,0,0);
|
|
gGL.begin(LLRender::LINES);
|
|
gGL.vertex3f(x * 0.1f, y * 0.1f ,0.f);
|
|
gGL.vertex3f(x, y, 0.f);
|
|
gGL.end();
|
|
gGL.popMatrix();
|
|
}
|
|
}
|
|
gGL.popMatrix();
|
|
}
|
|
|
|
|
|
|
|
|
|
// Used by lltoolselectland
|
|
void LLViewerParcelMgr::renderRect(const LLVector3d &west_south_bottom_global,
|
|
const LLVector3d &east_north_top_global )
|
|
{
|
|
LLGLSUIDefault gls_ui;
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
LLGLDepthTest gls_depth(GL_TRUE);
|
|
|
|
LLVector3 west_south_bottom_agent = gAgent.getPosAgentFromGlobal(west_south_bottom_global);
|
|
F32 west = west_south_bottom_agent.mV[VX];
|
|
F32 south = west_south_bottom_agent.mV[VY];
|
|
// F32 bottom = west_south_bottom_agent.mV[VZ] - 1.f;
|
|
|
|
LLVector3 east_north_top_agent = gAgent.getPosAgentFromGlobal(east_north_top_global);
|
|
F32 east = east_north_top_agent.mV[VX];
|
|
F32 north = east_north_top_agent.mV[VY];
|
|
// F32 top = east_north_top_agent.mV[VZ] + 1.f;
|
|
|
|
// HACK: At edge of last region of world, we need to make sure the region
|
|
// resolves correctly so we can get a height value.
|
|
const F32 FUDGE = 0.01f;
|
|
|
|
F32 sw_bottom = LLWorld::getInstance()->resolveLandHeightAgent( LLVector3( west, south, 0.f ) );
|
|
F32 se_bottom = LLWorld::getInstance()->resolveLandHeightAgent( LLVector3( east-FUDGE, south, 0.f ) );
|
|
F32 ne_bottom = LLWorld::getInstance()->resolveLandHeightAgent( LLVector3( east-FUDGE, north-FUDGE, 0.f ) );
|
|
F32 nw_bottom = LLWorld::getInstance()->resolveLandHeightAgent( LLVector3( west, north-FUDGE, 0.f ) );
|
|
|
|
F32 sw_top = sw_bottom + PARCEL_POST_HEIGHT;
|
|
F32 se_top = se_bottom + PARCEL_POST_HEIGHT;
|
|
F32 ne_top = ne_bottom + PARCEL_POST_HEIGHT;
|
|
F32 nw_top = nw_bottom + PARCEL_POST_HEIGHT;
|
|
|
|
LLUI::setLineWidth(2.f);
|
|
gGL.color4f(1.f, 1.f, 0.f, 1.f);
|
|
|
|
// Cheat and give this the same pick-name as land
|
|
gGL.begin(LLRender::LINES);
|
|
|
|
gGL.vertex3f(west, north, nw_bottom);
|
|
gGL.vertex3f(west, north, nw_top);
|
|
|
|
gGL.vertex3f(east, north, ne_bottom);
|
|
gGL.vertex3f(east, north, ne_top);
|
|
|
|
gGL.vertex3f(east, south, se_bottom);
|
|
gGL.vertex3f(east, south, se_top);
|
|
|
|
gGL.vertex3f(west, south, sw_bottom);
|
|
gGL.vertex3f(west, south, sw_top);
|
|
|
|
gGL.end();
|
|
|
|
gGL.color4f(1.f, 1.f, 0.f, 0.2f);
|
|
gGL.begin(LLRender::QUADS);
|
|
|
|
gGL.vertex3f(west, north, nw_bottom);
|
|
gGL.vertex3f(west, north, nw_top);
|
|
gGL.vertex3f(east, north, ne_top);
|
|
gGL.vertex3f(east, north, ne_bottom);
|
|
|
|
gGL.vertex3f(east, north, ne_bottom);
|
|
gGL.vertex3f(east, north, ne_top);
|
|
gGL.vertex3f(east, south, se_top);
|
|
gGL.vertex3f(east, south, se_bottom);
|
|
|
|
gGL.vertex3f(east, south, se_bottom);
|
|
gGL.vertex3f(east, south, se_top);
|
|
gGL.vertex3f(west, south, sw_top);
|
|
gGL.vertex3f(west, south, sw_bottom);
|
|
|
|
gGL.vertex3f(west, south, sw_bottom);
|
|
gGL.vertex3f(west, south, sw_top);
|
|
gGL.vertex3f(west, north, nw_top);
|
|
gGL.vertex3f(west, north, nw_bottom);
|
|
|
|
gGL.end();
|
|
|
|
LLUI::setLineWidth(1.f);
|
|
}
|
|
|
|
/*
|
|
void LLViewerParcelMgr::renderParcel(LLParcel* parcel )
|
|
{
|
|
S32 i;
|
|
S32 count = parcel->getBoxCount();
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
const LLParcelBox& box = parcel->getBox(i);
|
|
|
|
F32 west = box.mMin.mV[VX];
|
|
F32 south = box.mMin.mV[VY];
|
|
|
|
F32 east = box.mMax.mV[VX];
|
|
F32 north = box.mMax.mV[VY];
|
|
|
|
// HACK: At edge of last region of world, we need to make sure the region
|
|
// resolves correctly so we can get a height value.
|
|
const F32 FUDGE = 0.01f;
|
|
|
|
F32 sw_bottom = LLWorld::getInstance()->resolveLandHeightAgent( LLVector3( west, south, 0.f ) );
|
|
F32 se_bottom = LLWorld::getInstance()->resolveLandHeightAgent( LLVector3( east-FUDGE, south, 0.f ) );
|
|
F32 ne_bottom = LLWorld::getInstance()->resolveLandHeightAgent( LLVector3( east-FUDGE, north-FUDGE, 0.f ) );
|
|
F32 nw_bottom = LLWorld::getInstance()->resolveLandHeightAgent( LLVector3( west, north-FUDGE, 0.f ) );
|
|
|
|
// little hack to make nearby lines not Z-fight
|
|
east -= 0.1f;
|
|
north -= 0.1f;
|
|
|
|
F32 sw_top = sw_bottom + POST_HEIGHT;
|
|
F32 se_top = se_bottom + POST_HEIGHT;
|
|
F32 ne_top = ne_bottom + POST_HEIGHT;
|
|
F32 nw_top = nw_bottom + POST_HEIGHT;
|
|
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
LLGLDepthTest gls_depth(GL_TRUE);
|
|
|
|
LLUI::setLineWidth(2.f);
|
|
gGL.color4f(0.f, 1.f, 1.f, 1.f);
|
|
|
|
// Cheat and give this the same pick-name as land
|
|
gGL.begin(LLRender::LINES);
|
|
|
|
gGL.vertex3f(west, north, nw_bottom);
|
|
gGL.vertex3f(west, north, nw_top);
|
|
|
|
gGL.vertex3f(east, north, ne_bottom);
|
|
gGL.vertex3f(east, north, ne_top);
|
|
|
|
gGL.vertex3f(east, south, se_bottom);
|
|
gGL.vertex3f(east, south, se_top);
|
|
|
|
gGL.vertex3f(west, south, sw_bottom);
|
|
gGL.vertex3f(west, south, sw_top);
|
|
|
|
gGL.end();
|
|
|
|
gGL.color4f(0.f, 1.f, 1.f, 0.2f);
|
|
gGL.begin(LLRender::QUADS);
|
|
|
|
gGL.vertex3f(west, north, nw_bottom);
|
|
gGL.vertex3f(west, north, nw_top);
|
|
gGL.vertex3f(east, north, ne_top);
|
|
gGL.vertex3f(east, north, ne_bottom);
|
|
|
|
gGL.vertex3f(east, north, ne_bottom);
|
|
gGL.vertex3f(east, north, ne_top);
|
|
gGL.vertex3f(east, south, se_top);
|
|
gGL.vertex3f(east, south, se_bottom);
|
|
|
|
gGL.vertex3f(east, south, se_bottom);
|
|
gGL.vertex3f(east, south, se_top);
|
|
gGL.vertex3f(west, south, sw_top);
|
|
gGL.vertex3f(west, south, sw_bottom);
|
|
|
|
gGL.vertex3f(west, south, sw_bottom);
|
|
gGL.vertex3f(west, south, sw_top);
|
|
gGL.vertex3f(west, north, nw_top);
|
|
gGL.vertex3f(west, north, nw_bottom);
|
|
|
|
gGL.end();
|
|
|
|
LLUI::setLineWidth(1.f);
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
// north = a wall going north/south. Need that info to set up texture
|
|
// coordinates correctly.
|
|
void LLViewerParcelMgr::renderOneSegment(F32 x1, F32 y1, F32 x2, F32 y2, F32 height, U8 direction, LLViewerRegion* regionp)
|
|
{
|
|
// HACK: At edge of last region of world, we need to make sure the region
|
|
// resolves correctly so we can get a height value.
|
|
const F32 BORDER = REGION_WIDTH_METERS - 0.1f;
|
|
|
|
F32 clamped_x1 = x1;
|
|
F32 clamped_y1 = y1;
|
|
F32 clamped_x2 = x2;
|
|
F32 clamped_y2 = y2;
|
|
|
|
if (clamped_x1 > BORDER) clamped_x1 = BORDER;
|
|
if (clamped_y1 > BORDER) clamped_y1 = BORDER;
|
|
if (clamped_x2 > BORDER) clamped_x2 = BORDER;
|
|
if (clamped_y2 > BORDER) clamped_y2 = BORDER;
|
|
|
|
F32 z;
|
|
F32 z1;
|
|
F32 z2;
|
|
|
|
z1 = regionp->getLand().resolveHeightRegion( LLVector3( clamped_x1, clamped_y1, 0.f ) );
|
|
z2 = regionp->getLand().resolveHeightRegion( LLVector3( clamped_x2, clamped_y2, 0.f ) );
|
|
|
|
// Convert x1 and x2 from region-local to agent coords.
|
|
LLVector3 origin = regionp->getOriginAgent();
|
|
x1 += origin.mV[VX];
|
|
x2 += origin.mV[VX];
|
|
y1 += origin.mV[VY];
|
|
y2 += origin.mV[VY];
|
|
|
|
if (height < 1.f)
|
|
{
|
|
z = z1+height;
|
|
gGL.vertex3f(x1, y1, z);
|
|
|
|
gGL.vertex3f(x1, y1, z1);
|
|
|
|
gGL.vertex3f(x2, y2, z2);
|
|
|
|
z = z2+height;
|
|
gGL.vertex3f(x2, y2, z);
|
|
}
|
|
else
|
|
{
|
|
F32 tex_coord1;
|
|
F32 tex_coord2;
|
|
|
|
if (WEST_MASK == direction)
|
|
{
|
|
tex_coord1 = y1;
|
|
tex_coord2 = y2;
|
|
}
|
|
else if (SOUTH_MASK == direction)
|
|
{
|
|
tex_coord1 = x1;
|
|
tex_coord2 = x2;
|
|
}
|
|
else if (EAST_MASK == direction)
|
|
{
|
|
tex_coord1 = y2;
|
|
tex_coord2 = y1;
|
|
}
|
|
else /* (NORTH_MASK == direction) */
|
|
{
|
|
tex_coord1 = x2;
|
|
tex_coord2 = x1;
|
|
}
|
|
|
|
|
|
gGL.texCoord2f(tex_coord1*0.5f+0.5f, z1*0.5f);
|
|
gGL.vertex3f(x1, y1, z1);
|
|
|
|
gGL.texCoord2f(tex_coord2*0.5f+0.5f, z2*0.5f);
|
|
gGL.vertex3f(x2, y2, z2);
|
|
|
|
// top edge stairsteps
|
|
z = llmax(z2+height, z1+height);
|
|
gGL.texCoord2f(tex_coord2*0.5f+0.5f, z*0.5f);
|
|
gGL.vertex3f(x2, y2, z);
|
|
|
|
gGL.texCoord2f(tex_coord1*0.5f+0.5f, z*0.5f);
|
|
gGL.vertex3f(x1, y1, z);
|
|
}
|
|
}
|
|
|
|
|
|
void LLViewerParcelMgr::renderHighlightSegments(const U8* segments, LLViewerRegion* regionp)
|
|
{
|
|
S32 x, y;
|
|
F32 x1, y1; // start point
|
|
F32 x2, y2; // end point
|
|
bool has_segments = false;
|
|
|
|
LLGLSUIDefault gls_ui;
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
LLGLDepthTest gls_depth(GL_TRUE);
|
|
|
|
gGL.color4f(1.f, 1.f, 0.f, 0.2f);
|
|
|
|
const S32 STRIDE = (mParcelsPerEdge+1);
|
|
|
|
// Cheat and give this the same pick-name as land
|
|
|
|
|
|
for (y = 0; y < STRIDE; y++)
|
|
{
|
|
for (x = 0; x < STRIDE; x++)
|
|
{
|
|
U8 segment_mask = segments[x + y*STRIDE];
|
|
|
|
if (segment_mask & SOUTH_MASK)
|
|
{
|
|
x1 = x * PARCEL_GRID_STEP_METERS;
|
|
y1 = y * PARCEL_GRID_STEP_METERS;
|
|
|
|
x2 = x1 + PARCEL_GRID_STEP_METERS;
|
|
y2 = y1;
|
|
|
|
if (!has_segments)
|
|
{
|
|
has_segments = true;
|
|
gGL.begin(LLRender::QUADS);
|
|
}
|
|
renderOneSegment(x1, y1, x2, y2, PARCEL_POST_HEIGHT, SOUTH_MASK, regionp);
|
|
}
|
|
|
|
if (segment_mask & WEST_MASK)
|
|
{
|
|
x1 = x * PARCEL_GRID_STEP_METERS;
|
|
y1 = y * PARCEL_GRID_STEP_METERS;
|
|
|
|
x2 = x1;
|
|
y2 = y1 + PARCEL_GRID_STEP_METERS;
|
|
|
|
if (!has_segments)
|
|
{
|
|
has_segments = true;
|
|
gGL.begin(LLRender::QUADS);
|
|
}
|
|
renderOneSegment(x1, y1, x2, y2, PARCEL_POST_HEIGHT, WEST_MASK, regionp);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (has_segments)
|
|
{
|
|
gGL.end();
|
|
}
|
|
}
|
|
|
|
|
|
void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLViewerRegion* regionp)
|
|
{
|
|
|
|
S32 x, y;
|
|
F32 x1, y1; // start point
|
|
F32 x2, y2; // end point
|
|
F32 alpha = 0;
|
|
F32 dist = 0;
|
|
F32 dx, dy;
|
|
F32 collision_height;
|
|
|
|
const S32 STRIDE = (mParcelsPerEdge+1);
|
|
|
|
LLVector3 pos = gAgent.getPositionAgent();
|
|
|
|
F32 pos_x = pos.mV[VX];
|
|
F32 pos_y = pos.mV[VY];
|
|
|
|
LLGLSUIDefault gls_ui;
|
|
LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
|
|
LLGLDisable cull(GL_CULL_FACE);
|
|
|
|
if (mCollisionBanned == BA_BANNED ||
|
|
regionp->getRegionFlag(REGION_FLAGS_BLOCK_FLYOVER))
|
|
{
|
|
collision_height = BAN_HEIGHT;
|
|
}
|
|
else
|
|
{
|
|
collision_height = PARCEL_HEIGHT;
|
|
}
|
|
|
|
|
|
if (use_pass && (mCollisionBanned == BA_NOT_ON_LIST))
|
|
{
|
|
gGL.getTexUnit(0)->bind(mPassImage);
|
|
}
|
|
else
|
|
{
|
|
gGL.getTexUnit(0)->bind(mBlockedImage);
|
|
}
|
|
|
|
gGL.begin(LLRender::QUADS);
|
|
|
|
for (y = 0; y < STRIDE; y++)
|
|
{
|
|
for (x = 0; x < STRIDE; x++)
|
|
{
|
|
U8 segment_mask = segments[x + y*STRIDE];
|
|
U8 direction;
|
|
const F32 MAX_ALPHA = 0.95f;
|
|
const S32 DIST_OFFSET = 5;
|
|
const S32 MIN_DIST_SQ = DIST_OFFSET*DIST_OFFSET;
|
|
const S32 MAX_DIST_SQ = 169;
|
|
|
|
if (segment_mask & SOUTH_MASK)
|
|
{
|
|
x1 = x * PARCEL_GRID_STEP_METERS;
|
|
y1 = y * PARCEL_GRID_STEP_METERS;
|
|
|
|
x2 = x1 + PARCEL_GRID_STEP_METERS;
|
|
y2 = y1;
|
|
|
|
dy = (pos_y - y1) + DIST_OFFSET;
|
|
|
|
if (pos_x < x1)
|
|
dx = pos_x - x1;
|
|
else if (pos_x > x2)
|
|
dx = pos_x - x2;
|
|
else
|
|
dx = 0;
|
|
|
|
dist = dx*dx+dy*dy;
|
|
|
|
if (dist < MIN_DIST_SQ)
|
|
alpha = MAX_ALPHA;
|
|
else if (dist > MAX_DIST_SQ)
|
|
alpha = 0.0f;
|
|
else
|
|
alpha = 30/dist;
|
|
|
|
alpha = llclamp(alpha, 0.0f, MAX_ALPHA);
|
|
|
|
gGL.color4f(1.f, 1.f, 1.f, alpha);
|
|
|
|
if ((pos_y - y1) < 0) direction = SOUTH_MASK;
|
|
else direction = NORTH_MASK;
|
|
|
|
// avoid Z fighting
|
|
renderOneSegment(x1+0.1f, y1+0.1f, x2+0.1f, y2+0.1f, collision_height, direction, regionp);
|
|
|
|
}
|
|
|
|
if (segment_mask & WEST_MASK)
|
|
{
|
|
x1 = x * PARCEL_GRID_STEP_METERS;
|
|
y1 = y * PARCEL_GRID_STEP_METERS;
|
|
|
|
x2 = x1;
|
|
y2 = y1 + PARCEL_GRID_STEP_METERS;
|
|
|
|
dx = (pos_x - x1) + DIST_OFFSET;
|
|
|
|
if (pos_y < y1)
|
|
dy = pos_y - y1;
|
|
else if (pos_y > y2)
|
|
dy = pos_y - y2;
|
|
else
|
|
dy = 0;
|
|
|
|
dist = dx*dx+dy*dy;
|
|
|
|
if (dist < MIN_DIST_SQ)
|
|
alpha = MAX_ALPHA;
|
|
else if (dist > MAX_DIST_SQ)
|
|
alpha = 0.0f;
|
|
else
|
|
alpha = 30/dist;
|
|
|
|
alpha = llclamp(alpha, 0.0f, MAX_ALPHA);
|
|
|
|
gGL.color4f(1.f, 1.f, 1.f, alpha);
|
|
|
|
if ((pos_x - x1) > 0) direction = WEST_MASK;
|
|
else direction = EAST_MASK;
|
|
|
|
// avoid Z fighting
|
|
renderOneSegment(x1+0.1f, y1+0.1f, x2+0.1f, y2+0.1f, collision_height, direction, regionp);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
gGL.end();
|
|
}
|
|
|
|
void LLViewerParcelMgr::resetCollisionTimer()
|
|
{
|
|
mCollisionTimer.reset();
|
|
mRenderCollision = TRUE;
|
|
}
|
|
|
|
void draw_line_cube(F32 width, const LLVector3& center)
|
|
{
|
|
width = 0.5f * width;
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] + width);
|
|
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] - width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] - width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] - width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] - width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] - width);
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] - width);
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] - width);
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] - width);
|
|
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] - width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] - width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] - width);
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] + width);
|
|
gGL.vertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] - width);
|
|
}
|
|
|
|
void draw_cross_lines(const LLVector3& center, F32 dx, F32 dy, F32 dz)
|
|
{
|
|
gGL.vertex3f(center.mV[VX] - dx, center.mV[VY], center.mV[VZ]);
|
|
gGL.vertex3f(center.mV[VX] + dx, center.mV[VY], center.mV[VZ]);
|
|
gGL.vertex3f(center.mV[VX], center.mV[VY] - dy, center.mV[VZ]);
|
|
gGL.vertex3f(center.mV[VX], center.mV[VY] + dy, center.mV[VZ]);
|
|
gGL.vertex3f(center.mV[VX], center.mV[VY], center.mV[VZ] - dz);
|
|
gGL.vertex3f(center.mV[VX], center.mV[VY], center.mV[VZ] + dz);
|
|
}
|
|
|
|
void LLViewerObjectList::renderObjectBeacons()
|
|
{
|
|
if (mDebugBeacons.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLGLSUIDefault gls_ui;
|
|
|
|
gUIProgram.bind();
|
|
|
|
{
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
|
|
S32 last_line_width = -1;
|
|
// gGL.begin(LLRender::LINES); // Always happens in (line_width != last_line_width)
|
|
|
|
for (std::vector<LLDebugBeacon>::iterator iter = mDebugBeacons.begin(); iter != mDebugBeacons.end(); ++iter)
|
|
{
|
|
const LLDebugBeacon &debug_beacon = *iter;
|
|
LLColor4 color = debug_beacon.mColor;
|
|
color.mV[3] *= 0.25f;
|
|
S32 line_width = debug_beacon.mLineWidth;
|
|
if (line_width != last_line_width)
|
|
{
|
|
gGL.flush();
|
|
glLineWidth( (F32)line_width );
|
|
last_line_width = line_width;
|
|
}
|
|
|
|
const LLVector3 &thisline = debug_beacon.mPositionAgent;
|
|
|
|
gGL.begin(LLRender::LINES);
|
|
gGL.color4fv(linearColor4(color).mV);
|
|
draw_cross_lines(thisline, 2.0f, 2.0f, 50.f);
|
|
draw_line_cube(0.10f, thisline);
|
|
|
|
gGL.end();
|
|
}
|
|
}
|
|
|
|
{
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
LLGLDepthTest gls_depth(GL_TRUE);
|
|
|
|
S32 last_line_width = -1;
|
|
// gGL.begin(LLRender::LINES); // Always happens in (line_width != last_line_width)
|
|
|
|
for (std::vector<LLDebugBeacon>::iterator iter = mDebugBeacons.begin(); iter != mDebugBeacons.end(); ++iter)
|
|
{
|
|
const LLDebugBeacon &debug_beacon = *iter;
|
|
|
|
S32 line_width = debug_beacon.mLineWidth;
|
|
if (line_width != last_line_width)
|
|
{
|
|
gGL.flush();
|
|
glLineWidth( (F32)line_width );
|
|
last_line_width = line_width;
|
|
}
|
|
|
|
const LLVector3 &thisline = debug_beacon.mPositionAgent;
|
|
gGL.begin(LLRender::LINES);
|
|
gGL.color4fv(linearColor4(debug_beacon.mColor).mV);
|
|
draw_cross_lines(thisline, 0.5f, 0.5f, 0.5f);
|
|
draw_line_cube(0.10f, thisline);
|
|
|
|
gGL.end();
|
|
}
|
|
|
|
gGL.flush();
|
|
glLineWidth(1.f);
|
|
|
|
for (std::vector<LLDebugBeacon>::iterator iter = mDebugBeacons.begin(); iter != mDebugBeacons.end(); ++iter)
|
|
{
|
|
LLDebugBeacon &debug_beacon = *iter;
|
|
if (debug_beacon.mString == "")
|
|
{
|
|
continue;
|
|
}
|
|
LLHUDText *hud_textp = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
|
|
|
|
hud_textp->setZCompare(FALSE);
|
|
LLColor4 color;
|
|
color = debug_beacon.mTextColor;
|
|
color.mV[3] *= 1.f;
|
|
|
|
hud_textp->setString(debug_beacon.mString);
|
|
hud_textp->setColor(color);
|
|
hud_textp->setPositionAgent(debug_beacon.mPositionAgent);
|
|
debug_beacon.mHUDObject = hud_textp;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLSky::renderSunMoonBeacons(const LLVector3& pos_agent, const LLVector3& direction, LLColor4 color)
|
|
{
|
|
LLGLSUIDefault gls_ui;
|
|
gUIProgram.bind();
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
|
|
LLVector3 pos_end;
|
|
for (S32 i = 0; i < 3; ++i)
|
|
{
|
|
pos_end.mV[i] = pos_agent.mV[i] + (50 * direction.mV[i]);
|
|
}
|
|
glLineWidth(LLPipeline::DebugBeaconLineWidth);
|
|
gGL.begin(LLRender::LINES);
|
|
color.mV[3] *= 0.5f;
|
|
gGL.color4fv(color.mV);
|
|
draw_cross_lines(pos_agent, 0.5f, 0.5f, 0.5f);
|
|
draw_cross_lines(pos_end, 2.f, 2.f, 2.f);
|
|
gGL.vertex3fv(pos_agent.mV);
|
|
gGL.vertex3fv(pos_end.mV);
|
|
gGL.end();
|
|
|
|
gGL.flush();
|
|
glLineWidth(1.f);
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// gpu_benchmark() helper classes
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// This struct is used to ensure that once we call initProfile(), it will
|
|
// definitely be matched by a corresponding call to finishProfile(). It's
|
|
// a struct rather than a class simply because every member is public.
|
|
struct ShaderProfileHelper
|
|
{
|
|
ShaderProfileHelper()
|
|
{
|
|
LLGLSLShader::initProfile();
|
|
}
|
|
~ShaderProfileHelper()
|
|
{
|
|
LLGLSLShader::finishProfile(false);
|
|
}
|
|
};
|
|
|
|
// This helper class is used to ensure that each generateTextures() call
|
|
// is matched by a corresponding deleteTextures() call. It also handles
|
|
// the bindManual() calls using those textures.
|
|
class TextureHolder
|
|
{
|
|
public:
|
|
TextureHolder(U32 unit, U32 size) :
|
|
texUnit(gGL.getTexUnit(unit)),
|
|
source(size) // preallocate vector
|
|
{
|
|
// takes (count, pointer)
|
|
// &vector[0] gets pointer to contiguous array
|
|
LLImageGL::generateTextures(source.size(), &source[0]);
|
|
}
|
|
|
|
~TextureHolder()
|
|
{
|
|
// unbind
|
|
if (texUnit)
|
|
{
|
|
texUnit->unbind(LLTexUnit::TT_TEXTURE);
|
|
}
|
|
// ensure that we delete these textures regardless of how we exit
|
|
LLImageGL::deleteTextures(source.size(), &source[0]);
|
|
}
|
|
|
|
bool bind(U32 index)
|
|
{
|
|
if (texUnit) // should always be there with dummy (-1), but just in case
|
|
{
|
|
return texUnit->bindManual(LLTexUnit::TT_TEXTURE, source[index]);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
// capture which LLTexUnit we're going to use
|
|
LLTexUnit* texUnit;
|
|
|
|
// use std::vector for implicit resource management
|
|
std::vector<U32> source;
|
|
};
|
|
|
|
class ShaderBinder
|
|
{
|
|
public:
|
|
ShaderBinder(LLGLSLShader& shader) :
|
|
mShader(shader)
|
|
{
|
|
mShader.bind();
|
|
}
|
|
~ShaderBinder()
|
|
{
|
|
mShader.unbind();
|
|
}
|
|
|
|
private:
|
|
LLGLSLShader& mShader;
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// gpu_benchmark()
|
|
// returns measured memory bandwidth of GPU in gigabytes per second
|
|
//-----------------------------------------------------------------------------
|
|
F32 gpu_benchmark()
|
|
{
|
|
if (gGLManager.mGLVersion < 3.3f)
|
|
{ // don't bother benchmarking venerable drivers which don't support accurate timing anyway
|
|
return -1.f;
|
|
}
|
|
|
|
if (gBenchmarkProgram.mProgramObject == 0)
|
|
{
|
|
LLViewerShaderMgr::instance()->initAttribsAndUniforms();
|
|
|
|
gBenchmarkProgram.mName = "Benchmark Shader";
|
|
gBenchmarkProgram.mFeatures.attachNothing = true;
|
|
gBenchmarkProgram.mShaderFiles.clear();
|
|
gBenchmarkProgram.mShaderFiles.push_back(std::make_pair("interface/benchmarkV.glsl", GL_VERTEX_SHADER));
|
|
gBenchmarkProgram.mShaderFiles.push_back(std::make_pair("interface/benchmarkF.glsl", GL_FRAGMENT_SHADER));
|
|
gBenchmarkProgram.mShaderLevel = 1;
|
|
if (!gBenchmarkProgram.createShader(NULL, NULL))
|
|
{
|
|
return -1.f;
|
|
}
|
|
}
|
|
|
|
LLGLDisable blend(GL_BLEND);
|
|
|
|
//measure memory bandwidth by:
|
|
// - allocating a batch of textures and render targets
|
|
// - rendering those textures to those render targets
|
|
// - recording time taken
|
|
// - taking the median time for a given number of samples
|
|
|
|
//resolution of textures/render targets
|
|
const U32 res = 1024;
|
|
|
|
//number of textures
|
|
const U32 count = 32;
|
|
|
|
//number of samples to take
|
|
const S32 samples = 64;
|
|
|
|
//time limit, allocation operations shouldn't take longer then 30 seconds, same for actual benchmark.
|
|
const F32 time_limit = 30;
|
|
|
|
std::vector<LLRenderTarget> dest(count);
|
|
TextureHolder texHolder(0, count);
|
|
std::vector<F32> results;
|
|
|
|
//build a random texture
|
|
U8* pixels = new U8[res*res*4];
|
|
|
|
for (U32 i = 0; i < res*res*4; ++i)
|
|
{
|
|
pixels[i] = (U8) ll_rand(255);
|
|
}
|
|
|
|
gGL.setColorMask(true, true);
|
|
LLGLDepthTest depth(GL_FALSE);
|
|
|
|
LLTimer alloc_timer;
|
|
alloc_timer.start();
|
|
for (U32 i = 0; i < count; ++i)
|
|
{
|
|
//allocate render targets and textures
|
|
if (!dest[i].allocate(res, res, GL_RGBA))
|
|
{
|
|
LL_WARNS("Benchmark") << "Failed to allocate render target." << LL_ENDL;
|
|
// abandon the benchmark test
|
|
delete[] pixels;
|
|
return -1.f;
|
|
}
|
|
dest[i].bindTarget();
|
|
dest[i].clear();
|
|
dest[i].flush();
|
|
|
|
if (!texHolder.bind(i))
|
|
{
|
|
// can use a dummy value mDummyTexUnit = new LLTexUnit(-1);
|
|
LL_WARNS("Benchmark") << "Failed to bind tex unit." << LL_ENDL;
|
|
// abandon the benchmark test
|
|
delete[] pixels;
|
|
return -1.f;
|
|
}
|
|
LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_RGBA, res,res,GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
|
|
if (alloc_timer.getElapsedTimeF32() > time_limit)
|
|
{
|
|
// abandon the benchmark test
|
|
LL_WARNS("Benchmark") << "Allocation operation took longer then 30 seconds, stopping." << LL_ENDL;
|
|
delete[] pixels;
|
|
return -1.f;
|
|
}
|
|
}
|
|
|
|
delete [] pixels;
|
|
|
|
//make a dummy triangle to draw with
|
|
LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX);
|
|
|
|
if (!buff->allocateBuffer(3, 0))
|
|
{
|
|
LL_WARNS("Benchmark") << "Failed to allocate buffer during benchmark." << LL_ENDL;
|
|
// abandon the benchmark test
|
|
return -1.f;
|
|
}
|
|
|
|
LLStrider<LLVector3> v;
|
|
|
|
if (! buff->getVertexStrider(v))
|
|
{
|
|
LL_WARNS("Benchmark") << "GL LLVertexBuffer::getVertexStrider() returned false, "
|
|
<< "buff->getMappedData() is"
|
|
<< (buff->getMappedData()? " not" : "")
|
|
<< " NULL" << LL_ENDL;
|
|
// abandon the benchmark test
|
|
return -1.f;
|
|
}
|
|
|
|
// generate dummy triangle
|
|
v[0].set(-1, 1, 0);
|
|
v[1].set(-1, -3, 0);
|
|
v[2].set(3, 1, 0);
|
|
|
|
buff->unmapBuffer();
|
|
|
|
LLGLSLShader::unbind();
|
|
|
|
F32 time_passed = 0; // seconds
|
|
|
|
{ //run CPU timer benchmark
|
|
glFinish();
|
|
gBenchmarkProgram.bind();
|
|
for (S32 c = -1; c < samples && time_passed < time_limit; ++c)
|
|
{
|
|
LLTimer timer;
|
|
timer.start();
|
|
|
|
for (U32 i = 0; i < count; ++i)
|
|
{
|
|
dest[i].bindTarget();
|
|
texHolder.bind(i);
|
|
buff->setBuffer();
|
|
buff->drawArrays(LLRender::TRIANGLES, 0, 3);
|
|
dest[i].flush();
|
|
}
|
|
|
|
//wait for current batch of copies to finish
|
|
glFinish();
|
|
|
|
F32 time = timer.getElapsedTimeF32();
|
|
time_passed += time;
|
|
|
|
if (c >= 0) // <-- ignore the first sample as it tends to be artificially slow
|
|
{
|
|
//store result in gigabytes per second
|
|
F32 gb = (F32)((F64)(res * res * 8 * count)) / (1000000000);
|
|
F32 gbps = gb / time;
|
|
results.push_back(gbps);
|
|
}
|
|
}
|
|
gBenchmarkProgram.unbind();
|
|
}
|
|
|
|
std::sort(results.begin(), results.end());
|
|
|
|
F32 gbps = results[results.size()/2];
|
|
|
|
LL_INFOS("Benchmark") << "Memory bandwidth is " << llformat("%.3f", gbps) << " GB/sec according to CPU timers, " << (F32)results.size() << " tests took " << time_passed << " seconds" << LL_ENDL;
|
|
|
|
#if LL_DARWIN
|
|
if (gbps > 512.f)
|
|
{
|
|
LL_WARNS("Benchmark") << "Memory bandwidth is improbably high and likely incorrect; discarding result." << LL_ENDL;
|
|
//OSX is probably lying, discard result
|
|
return -1.f;
|
|
}
|
|
#endif
|
|
|
|
// run GPU timer benchmark
|
|
{
|
|
ShaderProfileHelper initProfile;
|
|
dest[0].bindTarget();
|
|
gBenchmarkProgram.bind();
|
|
for (S32 c = 0; c < samples; ++c)
|
|
{
|
|
for (U32 i = 0; i < count; ++i)
|
|
{
|
|
texHolder.bind(i);
|
|
buff->setBuffer();
|
|
buff->drawArrays(LLRender::TRIANGLES, 0, 3);
|
|
}
|
|
}
|
|
gBenchmarkProgram.unbind();
|
|
dest[0].flush();
|
|
}
|
|
|
|
F32 ms = gBenchmarkProgram.mTimeElapsed/1000000.f;
|
|
F32 seconds = ms/1000.f;
|
|
|
|
F64 samples_drawn = gBenchmarkProgram.mSamplesDrawn;
|
|
F32 samples_sec = (samples_drawn/1000000000.0)/seconds;
|
|
gbps = samples_sec*4; // 4 bytes per sample
|
|
|
|
LL_INFOS("Benchmark") << "Memory bandwidth is " << llformat("%.3f", gbps) << " GB/sec according to ARB_timer_query, total time " << seconds << " seconds" << LL_ENDL;
|
|
|
|
return gbps;
|
|
}
|