This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
+781
View File
@@ -0,0 +1,781 @@
#if defined(RBX_PLATFORM_IOS) || defined(__ANDROID__)
#include "DeviceGL.h"
#include "GfxCore/Framebuffer.h"
#include "G3D/Quat.h"
#include "G3D/Matrix4.h"
#include "rbx/threadsafe.h"
#include "rbx/CEvent.h"
#include "rbx/Profiler.h"
#include "HeadersGL.h"
#include <math.h>
#ifdef RBX_PLATFORM_IOS
#import <CoreMotion/CMMotionManager.h>
#import <UIKit/UIApplication.h>
#import <UIKit/UIScreen.h>
#import <QuartzCore/CABase.h>
#endif
#ifdef __ANDROID__
#include <android/sensor.h>
#include <EGL/egl.h>
#include <time.h>
#endif
LOGGROUP(VR)
FASTFLAGVARIABLE(CardboardVR, false)
namespace RBX
{
namespace Graphics
{
// Configuration (data is kept globally accessible for ease of use from the app)
struct CardboardConfiguration
{
float fieldOfView;
float interLensDistance;
float trayToLensCenterDistance; // aka verticalDistanceToLensCenter
float screenToLensDistance;
float distortionCoeffs[2];
float screenWidth;
float screenHeight;
float screenBorderSize;
};
// Google Cardboard - Technical Specification version 2.0 - September 2015
static CardboardConfiguration gConfiguration = { 120, 0.0639f, 0.035f, 0.039f, { 0.34f, 0.55f }, 0, 0, 0.003f };
static float gScreenOrientation = 1.f;
// Implementation
const bool kUseVsync = true;
const bool kUseTimeWarp = true;
const bool kUseAdaptivePrediction = true;
const bool kUseNeckModel = true;
using G3D::Quat;
using G3D::Vector3;
using G3D::Matrix4;
const Vector3 kNeckOffset(0, 0.075f, -0.0805f);
struct DistortionVertex
{
float x, y;
float u, v;
float fade;
};
static float clamp(float t, float a, float b)
{
return std::min(std::max(t, a), b);
}
static float distort(const CardboardConfiguration& configuration, float radius)
{
float result = 1.0f;
float factor = 1.0f;
for (size_t i = 0; i < ARRAYSIZE(configuration.distortionCoeffs); ++i)
{
factor *= radius * radius;
result += configuration.distortionCoeffs[i] * factor;
}
return radius * result;
}
static float distortInverse(const CardboardConfiguration& configuration, float radius)
{
float r0 = radius / 0.9f;
float r = radius * 0.9f;
float dr0 = radius - distort(configuration, r0);
while (fabsf(r - r0) > 0.0001f)
{
float dr = radius - distort(configuration, r);
float r2 = r - dr * ((r - r0) / (dr - dr0));
r0 = r;
r = r2;
dr0 = dr;
}
return r;
}
static void computeFOVPort(const CardboardConfiguration& configuration, float fovPort[4])
{
float fovTan = tanf(configuration.fieldOfView / 2 * (3.1415926f / 180));
float outerDistance = (configuration.screenWidth - configuration.interLensDistance) / 2.0f;
float innerDistance = configuration.interLensDistance / 2.0f;
float bottomDistance = configuration.trayToLensCenterDistance - configuration.screenBorderSize;
float topDistance = configuration.screenHeight + configuration.screenBorderSize - configuration.trayToLensCenterDistance;
fovPort[0] = std::min(fovTan, distort(configuration, topDistance / configuration.screenToLensDistance));
fovPort[1] = std::min(fovTan, distort(configuration, bottomDistance / configuration.screenToLensDistance));
fovPort[2] = std::min(fovTan, distort(configuration, outerDistance / configuration.screenToLensDistance));
fovPort[3] = std::min(fovTan, distort(configuration, innerDistance / configuration.screenToLensDistance));
}
static shared_ptr<Geometry> createDistortionMesh(Device* device, const CardboardConfiguration& configuration, int eye, unsigned int* outIndices, float outUVScaleOffset[4])
{
float eyeSign = (eye == 0) ? -1 : 1;
float fovPort[4];
computeFOVPort(configuration, fovPort);
float screenWidth = configuration.screenWidth / configuration.screenToLensDistance;
float screenHeight = configuration.screenHeight / configuration.screenToLensDistance;
float xEyeOffsetScreen = (configuration.screenWidth / 2.0f + eyeSign * configuration.interLensDistance / 2.0f) / configuration.screenToLensDistance;
float yEyeOffsetScreen = (configuration.trayToLensCenterDistance - configuration.screenBorderSize) / configuration.screenToLensDistance;
float textureWidth = fovPort[2] + fovPort[3];
float textureHeight = fovPort[0] + fovPort[1];
float xEyeOffsetTexture = (eye == 0) ? fovPort[2] : fovPort[3];
float yEyeOffsetTexture = fovPort[1];
float vignetteSizeTanAngle = 0.05f;
const int rows = 40;
const int cols = 40;
const unsigned int vertexCount = rows * cols;
const unsigned int indexCount = (rows - 1) * cols * 2 + (rows - 2);
std::vector<VertexLayout::Element> elements;
elements.push_back(VertexLayout::Element(0, offsetof(DistortionVertex, x), VertexLayout::Format_Float2, VertexLayout::Semantic_Position));
elements.push_back(VertexLayout::Element(0, offsetof(DistortionVertex, u), VertexLayout::Format_Float3, VertexLayout::Semantic_Texture));
shared_ptr<VertexLayout> layout = device->createVertexLayout(elements);
shared_ptr<VertexBuffer> vb = device->createVertexBuffer(sizeof(DistortionVertex), vertexCount, GeometryBuffer::Usage_Static);
DistortionVertex* vbptr = static_cast<DistortionVertex*>(vb->lock());
for (int row = 0; row < rows; ++row)
{
for (int col = 0; col < cols; ++col)
{
float uTexture = col / float(cols - 1);
float vTexture = row / float(rows - 1);
float xTexture = uTexture * textureWidth - xEyeOffsetTexture;
float yTexture = vTexture * textureHeight - yEyeOffsetTexture;
float rTexture = sqrtf(xTexture * xTexture + yTexture * yTexture);
float textureToScreen = (rTexture > 0.0f) ? distortInverse(configuration, rTexture) / rTexture : 1.0f;
float xScreen = xTexture * textureToScreen;
float yScreen = yTexture * textureToScreen;
float uScreen = (xScreen + xEyeOffsetScreen) / screenWidth;
float vScreen = (yScreen + yEyeOffsetScreen) / screenHeight;
float vignetteSizeTexture = vignetteSizeTanAngle / textureToScreen;
float dxTexture = xTexture + xEyeOffsetTexture - clamp(xTexture + xEyeOffsetTexture, vignetteSizeTexture, textureWidth - vignetteSizeTexture);
float dyTexture = yTexture + yEyeOffsetTexture - clamp(yTexture + yEyeOffsetTexture, vignetteSizeTexture, textureHeight - vignetteSizeTexture);
float drTexture = sqrtf(dxTexture * dxTexture + dyTexture * dyTexture);
float vignette = 1.0f - clamp(drTexture / vignetteSizeTexture, 0.0f, 1.0f);
vbptr->x = 2.0f * uScreen - 1.0f;
vbptr->y = 2.0f * vScreen - 1.0f;
vbptr->u = xTexture;
vbptr->v = yTexture;
vbptr->fade = vignette;
vbptr++;
}
}
vb->unlock();
shared_ptr<IndexBuffer> ib = device->createIndexBuffer(sizeof(unsigned short), indexCount, GeometryBuffer::Usage_Static);
unsigned short* ibptr = static_cast<unsigned short*>(ib->lock());
unsigned int vertexOffset = 0;
for (int row = 0; row < rows-1; ++row)
{
if (row > 0)
{
int last = ibptr[-1];
*ibptr++ = last;
}
for (int col = 0; col < cols; ++col)
{
if (col > 0)
{
if (row % 2 == 0)
vertexOffset++;
else
vertexOffset--;
}
*ibptr++ = vertexOffset;
*ibptr++ = vertexOffset + 40;
}
vertexOffset += 40;
}
ib->unlock();
*outIndices = indexCount;
outUVScaleOffset[0] = 1 / textureWidth;
outUVScaleOffset[1] = 1 / textureHeight;
outUVScaleOffset[2] = xEyeOffsetTexture / textureWidth;
outUVScaleOffset[3] = yEyeOffsetTexture / textureHeight;
return device->createGeometry(layout, vb, ib);
}
static shared_ptr<ShaderProgram> createDistortionProgram(Device* device)
{
std::string vertexSourceCommon = "\
uniform vec4 UVScaleOffset;\
uniform vec4 Warp;\
vec3 qtransform(vec4 q, vec3 v) { return v + 2.0*cross(cross(v, q.xyz) + q.w*v, q.xyz); } \
void main() {\
gl_Position = vertex;\
highp vec3 uvw = qtransform(Warp, vec3(uv0.xy, -1.0));\
texcoord = vec3((uvw.xy / -uvw.z) * UVScaleOffset.xy + UVScaleOffset.zw, uv0.z);\
}";
std::string vertexSourceGL2 = "attribute highp vec4 vertex;\nattribute highp vec3 uv0;\nvarying highp vec3 texcoord;\n" + vertexSourceCommon;
std::string vertexSourceGL3 = "#version 300 es\nin highp vec4 vertex;\nin highp vec3 uv0;\nout highp vec3 texcoord;\n" + vertexSourceCommon;
std::string fragmentSourceGL2 = "uniform sampler2D buffer;\nvarying highp vec3 texcoord;\nvoid main() { gl_FragData[0] = texture2D(buffer, texcoord.xy) * texcoord.z; }";
std::string fragmentSourceGL3 = "#version 300 es\nuniform sampler2D buffer;\nout lowp vec4 _glFragData[1];\nin highp vec3 texcoord;\nvoid main() { _glFragData[0] = texture(buffer, texcoord.xy) * texcoord.z; }";
std::string vertexSource = (device->getShadingLanguage() == "glsles") ? vertexSourceGL2 : vertexSourceGL3;
std::string fragmentSource = (device->getShadingLanguage() == "glsles") ? fragmentSourceGL2 : fragmentSourceGL3;
return device->createShaderProgram(
device->createVertexShader(device->createShaderBytecode(vertexSource, "", "main")),
device->createFragmentShader(device->createShaderBytecode(fragmentSource, "", "main")));
}
static Quat predictRotation(double displayTimestamp, double sensorTimestamp, const Vector3& rotationRate)
{
float predictionDt = std::min(std::max(displayTimestamp - sensorTimestamp, 0.0), 0.1);
if (kUseAdaptivePrediction)
{
float angularSpeed = rotationRate.length();
float candidateDt = angularSpeed * 0.2; // The rate at which the dynamic prediction interval varies
predictionDt = (angularSpeed > 0.001f) ? std::min(predictionDt, candidateDt) : 0;
}
return Quat::fromRotation(predictionDt * rotationRate);
}
static DeviceVR::Pose getPose(const Vector3& position, const Quat& orientation)
{
DeviceVR::Pose result = {};
result.valid = true;
result.position[0] = position.x;
result.position[1] = position.y;
result.position[2] = position.z;
result.orientation[0] = orientation.x;
result.orientation[1] = orientation.y;
result.orientation[2] = orientation.z;
result.orientation[3] = orientation.w;
return result;
}
#ifdef RBX_PLATFORM_IOS
class HeadTracker
{
public:
HeadTracker()
: gyroTimestamp(0)
, orientationTimestamp(0)
{
motionManager = [[CMMotionManager alloc] init];
queue = [[NSOperationQueue alloc] init];
motionManager.gyroUpdateInterval = 1 / 100.0;
[motionManager startGyroUpdatesToQueue:queue withHandler:^(CMGyroData* gyroData, NSError*) {
RBXPROFILER_SCOPE("VR", "updateSensors");
RBXPROFILER_LABELF("VR", "gyro %.1f ms", (gyroData.timestamp - CACurrentMediaTime()) * 1000);
rbx::spin_mutex::scoped_lock lock(mutex);
gyro = Vector3(gyroData.rotationRate.x, gyroData.rotationRate.y, gyroData.rotationRate.z);
gyroTimestamp = gyroData.timestamp;
}];
motionManager.deviceMotionUpdateInterval = 1 / 100.0;
[motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical toQueue:queue withHandler:^(CMDeviceMotion* motion, NSError*) {
RBXPROFILER_SCOPE("VR", "updateSensors");
RBXPROFILER_LABELF("VR", "orientation %.1f ms", (motion.timestamp - CACurrentMediaTime()) * 1000);
rbx::spin_mutex::scoped_lock lock(mutex);
orientation = Quat(motion.attitude.quaternion.x, motion.attitude.quaternion.y, motion.attitude.quaternion.z, motion.attitude.quaternion.w);
orientationTimestamp = motion.timestamp;
}];
}
~HeadTracker()
{
[motionManager stopGyroUpdates];
[motionManager stopDeviceMotionUpdates];
[motionManager release];
[queue release];
}
double getTime()
{
return CACurrentMediaTime();
}
bool isValid()
{
return orientationTimestamp > 0;
}
Quat predictOrientation(double time)
{
rbx::spin_mutex::scoped_lock lock(mutex);
if (!isValid()) return Quat();
return orientation * predictRotation(time, orientationTimestamp, gyro);
}
private:
CMMotionManager* motionManager;
NSOperationQueue* queue;
rbx::spin_mutex mutex;
Vector3 gyro;
double gyroTimestamp;
Quat orientation;
double orientationTimestamp;
};
#endif
#ifdef __ANDROID__
enum
{
ASENSOR_TYPE_GAME_ROTATION = 15,
};
class HeadTracker
{
public:
HeadTracker()
: looper(NULL)
, looperReady(false)
, gyroTimestamp(0)
, gameRotationTimestamp(0)
{
boost::thread(boost::bind(processThread, this)).swap(thread);
}
~HeadTracker()
{
looperReady.Wait();
ALooper_wake(looper);
thread.join();
}
double getTime()
{
timespec tv;
clock_gettime(CLOCK_BOOTTIME, &tv);
return tv.tv_sec + tv.tv_nsec / 1e9;
}
bool isValid()
{
return gameRotationTimestamp > 0;
}
Quat predictOrientation(double time)
{
rbx::spin_mutex::scoped_lock lock(mutex);
if (!isValid()) return Quat();
Quat orientation(gameRotation, sqrtf(std::max(0.f, 1 - gameRotation.squaredLength())));
return orientation * predictRotation(time, gameRotationTimestamp, gyro);
}
private:
static void processThread(HeadTracker* self)
{
Profiler::onThreadCreate("SensorUpdate");
self->looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
self->looperReady.Set();
ASensorManager* manager = ASensorManager_getInstance();
ASensorEventQueue* eventQueue = ASensorManager_createEventQueue(manager, self->looper, 0, NULL, NULL);
FASTLOG(FLog::VR, "VR: Sensor thread started");
setupSensor(eventQueue, ASensorManager_getDefaultSensor(manager, ASENSOR_TYPE_GYROSCOPE));
setupSensor(eventQueue, ASensorManager_getDefaultSensor(manager, ASENSOR_TYPE_GAME_ROTATION));
for (;;)
{
int rc = ALooper_pollOnce(-1, NULL, NULL, NULL);
if (rc != 0)
{
FASTLOG1(FLog::VR, "VR: Stopping sensor thread (pollOnce returned %d)", rc);
break;
}
RBXPROFILER_SCOPE("VR", "updateSensors");
for (;;)
{
ASensorEvent eventBuffer[16];
ssize_t numEvents = ASensorEventQueue_getEvents(eventQueue, eventBuffer, ARRAYSIZE(eventBuffer));
if (numEvents <= 0) break;
rbx::spin_mutex::scoped_lock lock(self->mutex);
double time = self->getTime();
for (ssize_t i = 0; i < numEvents; ++i)
{
const ASensorEvent& e = eventBuffer[i];
if (e.type == ASENSOR_TYPE_GYROSCOPE)
{
self->gyro = Vector3(e.data);
self->gyroTimestamp = e.timestamp / 1e9;
RBXPROFILER_LABELF("VR", "gyro %.1f ms", (time - e.timestamp / 1e9) * 1000);
}
else if (e.type == ASENSOR_TYPE_GAME_ROTATION)
{
self->gameRotation = Vector3(e.data);
self->gameRotationTimestamp = e.timestamp / 1e9;
RBXPROFILER_LABELF("VR", "orientation %.1f ms", (time - e.timestamp / 1e9) * 1000);
}
}
}
}
ASensorManager_destroyEventQueue(manager, eventQueue);
Profiler::onThreadExit();
}
static void setupSensor(ASensorEventQueue* eventQueue, const ASensor* sensor)
{
if (sensor)
{
int rate = ASensor_getMinDelay(sensor);
ASensorEventQueue_enableSensor(eventQueue, sensor);
ASensorEventQueue_setEventRate(eventQueue, sensor, rate);
FASTLOG2(FLog::VR, "VR: Setup sensor %d with rate %d us", ASensor_getType(sensor), ASensor_getMinDelay(sensor));
FASTLOGS(FLog::VR, "VR: Sensor %s", ASensor_getName(sensor));
}
}
ALooper* looper;
CEvent looperReady;
boost::thread thread;
rbx::spin_mutex mutex;
Vector3 gyro;
double gyroTimestamp;
Vector3 gameRotation;
double gameRotationTimestamp;
};
#endif
struct CardboardVRGL: DeviceVRGL
{
Framebuffer* mainFramebuffer;
shared_ptr<Framebuffer> fb[2];
shared_ptr<Texture> textures[2];
shared_ptr<ShaderProgram> distortionProgram;
shared_ptr<Geometry> distortionMesh[2];
unsigned int distortionMeshIndices;
float distortionUVScaleOffset[2][4];
HeadTracker headTracker;
CardboardConfiguration configuration;
double displayTime;
Quat headOrientation;
Vector3 headPosition;
float headingRef;
bool headingSet;
CardboardVRGL()
{
mainFramebuffer = NULL;
distortionMeshIndices = 0;
memset(distortionUVScaleOffset, 0, sizeof(distortionUVScaleOffset));
displayTime = 0;
headingRef = 0;
headingSet = 0;
}
~CardboardVRGL()
{
}
Quat getHeadOrientation(const Quat& orientation)
{
float pi = G3D::pif();
float uiOrientationFlip = gScreenOrientation;
Quat displayOrientation = Quat::fromAxisAngleRotation(Vector3::unitZ(), uiOrientationFlip * pi / 2);
return
Quat::fromAxisAngleRotation(Vector3::unitX(), -pi / 2) *
orientation * displayOrientation;
}
Quat getHeadOrientationCentered(const Quat& orientation)
{
return
Quat::fromAxisAngleRotation(Vector3::unitY(), -headingRef) *
getHeadOrientation(orientation);
}
void update() override
{
// 40 ms is the estimated time to display the content
displayTime = headTracker.getTime() + 0.040;
Quat orientation = headTracker.predictOrientation(displayTime);
// reset heading when we get valid orientations
if (!headingSet && headTracker.isValid())
{
Vector3 heading = getHeadOrientation(orientation) * Vector3(0, 0, -1);
if (fabsf(heading.y) < 0.95f)
{
float angle = atan2(-heading.x, -heading.z);
headingRef = angle;
headingSet = true;
}
}
// update head orientation
headOrientation = getHeadOrientationCentered(orientation);
if (kUseNeckModel)
{
// update head position using neck model
headPosition = headOrientation * kNeckOffset - Vector3(0, kNeckOffset.y, 0);
}
}
void recenter() override
{
headingSet = false;
}
Framebuffer* getEyeFramebuffer(int eye) override
{
RBXASSERT(eye == 0 || eye == 1);
return fb[eye].get();
}
State getState() override
{
State result = {};
result.headPose = getPose(headPosition, headOrientation);
float fovPort[4];
computeFOVPort(configuration, fovPort);
for (int eye = 0; eye < 2; ++eye)
{
float eyeSign = (eye == 0 ? -1 : 1);
result.eyeOffset[eye][0] = eyeSign * configuration.interLensDistance / 2;
result.eyeOffset[eye][1] = 0;
result.eyeOffset[eye][2] = 0;
result.eyeFov[eye][0] = fovPort[0];
result.eyeFov[eye][1] = fovPort[1];
result.eyeFov[eye][2] = (eye == 0) ? fovPort[2] : fovPort[3];
result.eyeFov[eye][3] = (eye == 0) ? fovPort[3] : fovPort[2];
}
result.needsMirror = false;
return result;
}
void submitFrame(DeviceContext* context) override
{
unsigned int screenWidth = mainFramebuffer->getWidth();
unsigned int screenHeight = mainFramebuffer->getHeight();
context->bindFramebuffer(mainFramebuffer);
const float clearColor[] = {0, 0, 0, 0};
context->clearFramebuffer(DeviceContext::Buffer_Color, clearColor, 0, 0);
context->bindProgram(distortionProgram.get());
context->setBlendState(BlendState::Mode_None);
context->setRasterizerState(RasterizerState::Cull_None);
context->setDepthState(DepthState(DepthState::Function_Always, false));
Quat warp;
if (kUseTimeWarp)
{
Quat warpOrientation = headTracker.predictOrientation(displayTime);
Quat warpHeadOrientation = getHeadOrientationCentered(warpOrientation);
warp = warpHeadOrientation.inverse() * headOrientation;
}
for (int eye = 0; eye < 2; ++eye)
{
context->setConstant(distortionProgram->getConstantHandle("UVScaleOffset"), distortionUVScaleOffset[eye], 1);
context->setConstant(distortionProgram->getConstantHandle("Warp"), &warp.x, 1);
context->bindTexture(0, textures[eye].get(), SamplerState(SamplerState::Filter_Linear, SamplerState::Address_Clamp));
context->draw(distortionMesh[eye].get(), Geometry::Primitive_TriangleStrip, 0, distortionMeshIndices, 0, 0);
}
const int dividerWidth = 4;
const float dividerColor[] = {1, 1, 1, 1};
glEnable(GL_SCISSOR_TEST);
glScissor(screenWidth / 2 - dividerWidth / 2, 0, dividerWidth, screenHeight);
context->clearFramebuffer(DeviceContext::Buffer_Color, dividerColor, 0, 0);
glDisable(GL_SCISSOR_TEST);
}
void setup(Device* device) override
{
mainFramebuffer = device->getMainFramebuffer();
unsigned int width = 1024, height = 1024;
shared_ptr<Renderbuffer> depthStencil = device->createRenderbuffer(Texture::Format_D24S8, width, height, 1);
for (int eye = 0; eye < 2; ++eye)
{
textures[eye] = device->createTexture(Texture::Type_2D, Texture::Format_RGBA8, width, height, 1, 1, Texture::Usage_Renderbuffer);
fb[eye] = device->createFramebuffer(textures[eye]->getRenderbuffer(0, 0), depthStencil);
}
distortionMesh[0] = createDistortionMesh(device, configuration, 0, &distortionMeshIndices, distortionUVScaleOffset[0]);
distortionMesh[1] = createDistortionMesh(device, configuration, 1, &distortionMeshIndices, distortionUVScaleOffset[1]);
distortionProgram = createDistortionProgram(device);
}
};
DeviceVRGL* DeviceVRGL::createCardboard()
{
if (!FFlag::CardboardVR)
return NULL;
CardboardVRGL* vr = new CardboardVRGL();
vr->configuration = gConfiguration;
float fovPort[4];
computeFOVPort(vr->configuration, fovPort);
for (int i = 0; i < 4; ++i)
fovPort[i] = atanf(fovPort[i]) / (3.1415926f / 180);
#ifdef __ANDROID__
eglSwapInterval(eglGetCurrentDisplay(), kUseVsync);
#else
(void)kUseVsync;
#endif
FASTLOG3F(FLog::VR, "VR: HMD interLens %f trayToLensCenter %f screenToLens %f", vr->configuration.interLensDistance, vr->configuration.trayToLensCenterDistance, vr->configuration.screenToLensDistance);
FASTLOG3F(FLog::VR, "VR: Lens FOV %f distortion0 %f distortion1 %f", vr->configuration.fieldOfView, vr->configuration.distortionCoeffs[0], vr->configuration.distortionCoeffs[1]);
FASTLOG3F(FLog::VR, "VR: Screen width %f height %f border %f", vr->configuration.screenWidth, vr->configuration.screenHeight, vr->configuration.screenBorderSize);
FASTLOG4F(FLog::VR, "VR: Eye FOV up %f down %f left %f right %f", fovPort[0], fovPort[1], fovPort[2], fovPort[3]);
return vr;
}
}
}
void vrCardboardSetDeviceParams(float fieldOfView, float interLensDistance, float trayToLensCenterDistance, float screenToLensDistance, float distortionCoeff0, float distortionCoeff1)
{
using RBX::Graphics::gConfiguration;
gConfiguration.fieldOfView = fieldOfView;
gConfiguration.interLensDistance = interLensDistance;
gConfiguration.trayToLensCenterDistance = trayToLensCenterDistance;
gConfiguration.screenToLensDistance = screenToLensDistance;
gConfiguration.distortionCoeffs[0] = distortionCoeff0;
gConfiguration.distortionCoeffs[1] = distortionCoeff1;
}
void vrCardboardSetPhoneParams(int screenWidth, int screenHeight, float xdpi, float ydpi)
{
using RBX::Graphics::gConfiguration;
float metersPerInch = 0.0254f;
float screenWidthMeters = screenWidth * (metersPerInch / xdpi);
float screenHeightMeters = screenHeight * (metersPerInch / ydpi);
gConfiguration.screenWidth = std::max(screenWidthMeters, screenHeightMeters);
gConfiguration.screenHeight = std::min(screenWidthMeters, screenHeightMeters);
gConfiguration.screenBorderSize = 0.003f;
}
void vrCardboardSetOrientation(bool flipped)
{
using RBX::Graphics::gScreenOrientation;
gScreenOrientation = flipped ? -1 : 1;
}
#endif
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <utility>
namespace RBX
{
namespace Graphics
{
class ContextGL
{
public:
static ContextGL* create(void* windowHandle);
virtual ~ContextGL() {}
virtual void setCurrent() = 0;
virtual void swapBuffers() = 0;
virtual unsigned int getMainFramebufferId() = 0;
virtual bool isMainFramebufferRetina() = 0;
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height) = 0;
};
}
}
+290
View File
@@ -0,0 +1,290 @@
#include "ContextGL.h"
#include "HeadersGL.h"
#include "rbx/Debug.h"
#include <android/native_window.h>
#include <EGL/egl.h>
LOGGROUP(Graphics)
// GL extensions
static GLvoid (GL_APIENTRY * glBindVertexArrayPtr) (GLuint array);
static GLvoid (GL_APIENTRY * glDeleteVertexArraysPtr) (GLsizei n, const GLuint* arrays);
static GLvoid (GL_APIENTRY * glGenVertexArraysPtr) (GLsizei n, const GLuint* arrays);
static GLvoid * (GL_APIENTRY * glMapBufferPtr) (GLenum target, GLenum access);
static GLboolean (GL_APIENTRY * glUnmapBufferPtr) (GLenum target);
static GLvoid * (GL_APIENTRY * glMapBufferRangePtr) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
static GLvoid (GL_APIENTRY * glTexImage3DPtr) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
static GLvoid (GL_APIENTRY * glTexSubImage3DPtr) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
static GLvoid (GL_APIENTRY * glTexStorage2DPtr) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
static GLvoid (GL_APIENTRY * glTexStorage3DPtr) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
static GLsync (GL_APIENTRY * glFenceSyncPtr) (GLenum condition, GLbitfield flags);
static void (GL_APIENTRY * glDeleteSyncPtr) (GLsync sync);
static GLenum (GL_APIENTRY * glClientWaitSyncPtr) (GLsync sync, GLbitfield flags, GLuint64 timeout);
static void (GL_APIENTRY * glWaitSyncPtr) (GLsync sync, GLbitfield flags, GLuint64 timeout);
static GLvoid (GL_APIENTRY * glBlitFramebufferPtr) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
static GLvoid (GL_APIENTRY * glRenderbufferStorageMultisamplePtr) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
static GLvoid (GL_APIENTRY * glInvalidateFramebufferPtr) (GLenum target, GLsizei numAttachments, const GLenum* attachments);
template <typename T> static void loadExtensionGL(T& ptr, const char* namecore, const char* nameext)
{
if (!ptr)
ptr = reinterpret_cast<T>(eglGetProcAddress(namecore));
if (!ptr)
ptr = reinterpret_cast<T>(eglGetProcAddress(nameext));
}
#define LOAD_EXTENSION_GL(name, suffix) loadExtensionGL(name ## Ptr, #name, #name #suffix)
// GL stubs
GLvoid glBindVertexArray(GLuint array)
{
glBindVertexArrayPtr(array);
}
GLvoid glDeleteVertexArrays(GLsizei n, const GLuint *arrays)
{
glDeleteVertexArraysPtr(n, arrays);
}
GLvoid glGenVertexArrays(GLsizei n, GLuint *arrays)
{
glGenVertexArraysPtr(n, arrays);
}
GLvoid *glMapBuffer(GLenum target, GLenum access)
{
return glMapBufferPtr(target, access);
}
GLboolean glUnmapBuffer(GLenum target)
{
return glUnmapBufferPtr(target);
}
GLvoid *glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
{
return glMapBufferRangePtr(target, offset, length, access);
}
GLvoid glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
{
glTexStorage2DPtr(target, levels, internalformat, width, height);
}
GLvoid glTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
{
glTexImage3DPtr(target, level, internalFormat, width, height, depth, border, format, type, pixels);
}
GLvoid glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels)
{
glTexSubImage3DPtr(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
}
GLvoid glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
{
glTexStorage3DPtr(target, levels, internalformat, width, height, depth);
}
GLsync glFenceSync(GLenum condition, GLbitfield flags)
{
return glFenceSyncPtr(condition, flags);
}
void glDeleteSync(GLsync sync)
{
glDeleteSyncPtr(sync);
}
GLenum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
{
return glClientWaitSyncPtr(sync, flags, timeout);
}
void glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
{
glWaitSyncPtr(sync, flags, timeout);
}
GLvoid glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
{
glBlitFramebufferPtr(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
GLvoid glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
{
glRenderbufferStorageMultisamplePtr(target, samples, internalformat, width, height);
}
GLvoid glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments)
{
glInvalidateFramebufferPtr(target, numAttachments, attachments);
}
namespace RBX
{
namespace Graphics
{
class ContextGLAndroid: public ContextGL
{
public:
ContextGLAndroid(void* windowHandle)
{
aNativeWindow = static_cast<ANativeWindow*>(windowHandle);
ANativeWindow_acquire(aNativeWindow);
FASTLOG2(FLog::Graphics, "Window size: %dx%d", ANativeWindow_getWidth(aNativeWindow), ANativeWindow_getHeight(aNativeWindow));
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY)
throw runtime_error("Error creating context: eglGetDisplay %x", eglGetError());
if (!eglInitialize(display, 0, 0))
throw runtime_error("Error creating context: eglInitialize %x", eglGetError());
EGLConfig config;
if (
!tryChooseConfig(display, &config, 8, 8, 8, 24, 0) &&
!tryChooseConfig(display, &config, 8, 8, 8, 16, 0) &&
!tryChooseConfig(display, &config, 5, 6, 5, 16, 0) &&
!tryChooseConfig(display, &config, 8, 8, 8, 24, 1) &&
!tryChooseConfig(display, &config, 8, 8, 8, 16, 1) &&
!tryChooseConfig(display, &config, 5, 6, 5, 16, 1))
throw runtime_error("Error creating context: could not find suitable config (%x)", eglGetError());
surface = eglCreateWindowSurface(display, config, aNativeWindow, 0);
if (!surface)
{
throw runtime_error("Error creating context: eglCreateWindowSurface %x", eglGetError());
}
static const EGLint contextAttrs[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
context = eglCreateContext(display, config, NULL, contextAttrs);
if (!context)
throw runtime_error("Error creating context: eglCreateContext %x", eglGetError());
if (!eglMakeCurrent(display, surface, surface, context))
throw runtime_error("Error creating context: eglMakeCurrent %x", eglGetError());
eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth);
eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight);
FASTLOG4(FLog::Graphics, "Initialized EGL context %p (surface %p) with renderbuffer %dx%d", context, surface, surfaceWidth, surfaceHeight);
EGLint minSwapInterval = -1;
eglGetConfigAttrib( display, config, EGL_MIN_SWAP_INTERVAL, &minSwapInterval );
FASTLOG1(FLog::Graphics, "EGL_MIN_SWAP_INTERVAL: %d", minSwapInterval );
if( eglSwapInterval( display, 0 ) == EGL_FALSE )
{
// This should be impossible as the interval is silently clamped to EGL_MIN_SWAP_INTERVAL.
FASTLOG(FLog::Graphics, "*** eglSwapInterval EGL_FALSE" );
}
LOAD_EXTENSION_GL(glBindVertexArray, OES);
LOAD_EXTENSION_GL(glDeleteVertexArrays, OES);
LOAD_EXTENSION_GL(glGenVertexArrays, OES);
LOAD_EXTENSION_GL(glMapBuffer, OES);
LOAD_EXTENSION_GL(glUnmapBuffer, OES);
LOAD_EXTENSION_GL(glMapBufferRange, EXT);
LOAD_EXTENSION_GL(glTexImage3D, OES);
LOAD_EXTENSION_GL(glTexSubImage3D, OES);
LOAD_EXTENSION_GL(glTexStorage2D, EXT);
LOAD_EXTENSION_GL(glTexStorage3D, EXT);
LOAD_EXTENSION_GL(glFenceSync, EXT);
LOAD_EXTENSION_GL(glDeleteSync, EXT);
LOAD_EXTENSION_GL(glClientWaitSync, EXT);
LOAD_EXTENSION_GL(glWaitSync, EXT);
LOAD_EXTENSION_GL(glBlitFramebuffer, EXT);
LOAD_EXTENSION_GL(glRenderbufferStorageMultisample, EXT);
LOAD_EXTENSION_GL(glInvalidateFramebuffer, EXT);
}
~ContextGLAndroid()
{
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(display, context);
eglDestroySurface(display, surface);
eglTerminate(display);
ANativeWindow_release(aNativeWindow);
}
virtual void setCurrent()
{
bool result = eglMakeCurrent(display, surface, surface, context);
RBXASSERT(result);
}
virtual void swapBuffers()
{
bool result = eglSwapBuffers(display, surface);
RBXASSERT(result);
}
virtual unsigned int getMainFramebufferId()
{
return 0;
}
virtual bool isMainFramebufferRetina()
{
return false;
}
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height)
{
return std::make_pair(surfaceWidth, surfaceHeight);
}
private:
ANativeWindow* aNativeWindow;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
EGLint surfaceWidth;
EGLint surfaceHeight;
static bool tryChooseConfig(EGLDisplay display, EGLConfig* config, int redBits, int greenBits, int blueBits, int depthBits, int swapInterval )
{
const EGLint attribs[] =
{
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_MIN_SWAP_INTERVAL, swapInterval,
EGL_RED_SIZE, redBits,
EGL_GREEN_SIZE, greenBits,
EGL_BLUE_SIZE, blueBits,
EGL_DEPTH_SIZE, depthBits,
EGL_NONE
};
FASTLOG4(FLog::Graphics, "Trying to choose EGL config r%d g%d b%d d%d", redBits, greenBits, blueBits, depthBits);
EGLint numConfigs = 0;
return eglChooseConfig(display, attribs, config, 1, &numConfigs) && numConfigs > 0;
}
};
ContextGL* ContextGL::create(void* windowHandle)
{
return new ContextGLAndroid(windowHandle);
}
}
}
+173
View File
@@ -0,0 +1,173 @@
#include "ContextGL.h"
#include "HeadersGL.h"
#include "rbx/Debug.h"
#include <AppKit/AppKit.h>
#include <OpenGL/OpenGL.h>
LOGGROUP(Graphics)
FASTFLAGVARIABLE(UpdateContextOnFollowingFrame, false)
FASTFLAG(GraphicsGL3)
namespace RBX
{
namespace Graphics
{
static NSOpenGLPixelFormat* createPixelFormatWithApi(unsigned int api)
{
NSOpenGLPixelFormatAttribute attribs[32];
int ai = 0;
// Specify the display ID to associate the GL context with main display. Useful if there is ambiguity
attribs[ai++] = NSOpenGLPFAScreenMask;
attribs[ai++] = CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID());
// Specifying "NoRecovery" gives us a context that cannot fall back to the software renderer.
attribs[ai++] = NSOpenGLPFANoRecovery;
attribs[ai++] = NSOpenGLPFAAccelerated;
attribs[ai++] = NSOpenGLPFADoubleBuffer;
attribs[ai++] = NSOpenGLPFAColorSize;
attribs[ai++] = 24;
attribs[ai++] = NSOpenGLPFAAlphaSize;
attribs[ai++] = 8;
attribs[ai++] = NSOpenGLPFAStencilSize;
attribs[ai++] = 8;
attribs[ai++] = NSOpenGLPFADepthSize;
attribs[ai++] = 16;
if (api)
{
attribs[ai++] = NSOpenGLPFAOpenGLProfile;
attribs[ai++] = api;
}
attribs[ai++] = 0;
return [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs];
}
class ContextGLNSGL: public ContextGL
{
public:
ContextGLNSGL(void* windowHandle)
: view(0)
, pixelFormat(0)
, context(0)
, updateRequired(false)
{
view = [(NSView*)windowHandle retain];
RBXASSERT(view);
if (FFlag::GraphicsGL3)
{
// OSX 10.7 graphics drivers crash on compiling some (valid) shaders
if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_8)
pixelFormat = createPixelFormatWithApi(NSOpenGLProfileVersion3_2Core);
if (!pixelFormat)
pixelFormat = createPixelFormatWithApi(0);
if (!pixelFormat)
throw std::runtime_error("Error creating pixel format");
}
else
{
pixelFormat = createPixelFormatWithApi(0);
if (!pixelFormat)
throw std::runtime_error("Error creating pixel format");
}
context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
if (!context)
throw std::runtime_error("Error creating context");
GLint swapInterval = 0;
[context setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
[context setView:view];
[context makeCurrentContext];
if (CGLError err = CGLEnable(static_cast<CGLContextObj>([context CGLContextObj]), kCGLCEMPEngine))
FASTLOG1(FLog::Graphics, "Error enabling MP engine: %x", err);
glewInitRBX();
}
~ContextGLNSGL()
{
if ([NSOpenGLContext currentContext] == context)
[NSOpenGLContext clearCurrentContext];
[context clearDrawable];
[context release];
[pixelFormat release];
[view release];
}
virtual void setCurrent()
{
if ([NSOpenGLContext currentContext] != context)
[context makeCurrentContext];
}
virtual void swapBuffers()
{
[context flushBuffer];
}
virtual unsigned int getMainFramebufferId()
{
return 0;
}
virtual bool isMainFramebufferRetina()
{
return false;
}
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height)
{
NSRect b = [view bounds];
if (FFlag::UpdateContextOnFollowingFrame && updateRequired)
{
[context update];
updateRequired = false;
}
if (width != b.size.width || height != b.size.height)
{
if (!FFlag::UpdateContextOnFollowingFrame)
[context update];
updateRequired = true;
}
return std::make_pair(b.size.width, b.size.height);
}
private:
NSView* view;
NSOpenGLPixelFormat* pixelFormat;
NSOpenGLContext* context;
bool updateRequired;
};
ContextGL* ContextGL::create(void* windowHandle)
{
return new ContextGLNSGL(windowHandle);
}
}
}
+138
View File
@@ -0,0 +1,138 @@
#include "ContextGL.h"
#include "HeadersGL.h"
#include "rbx/Debug.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <gl/wglew.h>
GLenum GLEWAPIENTRY wglewContextInit();
FASTFLAG(DebugGraphicsGL)
namespace RBX
{
namespace Graphics
{
class ContextGLWin32: public ContextGL
{
public:
ContextGLWin32(void* windowHandle)
: hwnd(reinterpret_cast<HWND>(windowHandle))
, hdc(GetDC(hwnd))
, hglrc(NULL)
{
PIXELFORMATDESCRIPTOR pfd = {};
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cAlphaBits = 8;
pfd.cDepthBits = 24;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
int pf = ChoosePixelFormat(hdc, &pfd);
if (!pf)
throw RBX::runtime_error("Error choosing pixel format: %x", GetLastError());
if (!SetPixelFormat(hdc, pf, &pfd))
throw RBX::runtime_error("Error setting pixel format: %x", GetLastError());
hglrc = wglCreateContext(hdc);
if (!hglrc)
throw RBX::runtime_error("Error creating context: %x", GetLastError());
if (!wglMakeCurrent(hdc, hglrc))
throw RBX::runtime_error("Error changing context: %x", GetLastError());
if (FFlag::DebugGraphicsGL)
{
// We need to initialize the context with wgl function, but you can only get the wgl function after you initialize the context.
// Let's just reinitialize it.
wglewContextInit();
if (WGLEW_ARB_create_context)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hglrc);
const int attribs[4] = { WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, 0, 0 };
hglrc = wglCreateContextAttribsARB(hdc, NULL, attribs);
if (!hglrc)
throw RBX::runtime_error("Error creating context: %x", GetLastError());
if (!wglMakeCurrent(hdc, hglrc))
throw RBX::runtime_error("Error changing context: %x", GetLastError());
}
}
glewInitRBX();
if (wglSwapIntervalEXT)
wglSwapIntervalEXT(0);
}
~ContextGLWin32()
{
if (wglGetCurrentContext() == hglrc)
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hglrc);
}
virtual void setCurrent()
{
if (wglGetCurrentContext() != hglrc)
{
BOOL result = wglMakeCurrent(hdc, hglrc);
RBXASSERT(result);
}
}
virtual void swapBuffers()
{
RBXASSERT(wglGetCurrentContext() == hglrc);
SwapBuffers(hdc);
}
virtual unsigned int getMainFramebufferId()
{
return 0;
}
virtual bool isMainFramebufferRetina()
{
return false;
}
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height)
{
RECT rect = {};
GetClientRect(hwnd, &rect);
return std::make_pair(rect.right - rect.left, rect.bottom - rect.top);
}
private:
HWND hwnd;
HDC hdc;
HGLRC hglrc;
};
ContextGL* ContextGL::create(void* windowHandle)
{
return new ContextGLWin32(windowHandle);
}
}
}
+259
View File
@@ -0,0 +1,259 @@
#ifdef RBX_PLATFORM_IOS
#include "ContextGL.h"
#include "HeadersGL.h"
#include "rbx/Debug.h"
#include <UIKit/UIKit.h>
LOGGROUP(Graphics)
FASTFLAG(GraphicsGL3)
// GL imports
namespace GLES3
{
extern "C" GLvoid glTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
extern "C" GLvoid glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
extern "C" GLvoid glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
extern "C" GLvoid glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
extern "C" GLvoid glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
extern "C" GLvoid glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments);
}
// GL stubs
GLvoid glBindVertexArray(GLuint array)
{
glBindVertexArrayOES(array);
}
GLvoid glDeleteVertexArrays(GLsizei n, const GLuint *arrays)
{
glDeleteVertexArraysOES(n, arrays);
}
GLvoid glGenVertexArrays(GLsizei n, GLuint *arrays)
{
glGenVertexArraysOES(n, arrays);
}
GLvoid *glMapBuffer(GLenum target, GLenum access)
{
return glMapBufferOES(target, access);
}
GLboolean glUnmapBuffer(GLenum target)
{
return glUnmapBufferOES(target);
}
GLvoid *glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
{
return glMapBufferRangeEXT(target, offset, length, access);
}
GLvoid glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
{
glTexStorage2DEXT(target, levels, internalformat, width, height);
}
GLvoid glTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
{
GLES3::glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, pixels);
}
GLvoid glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels)
{
GLES3::glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
}
GLvoid glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
{
GLES3::glTexStorage3D(target, levels, internalformat, width, height, depth);
}
GLsync glFenceSync(GLenum condition, GLbitfield flags)
{
return glFenceSyncAPPLE(condition, flags);
}
void glDeleteSync(GLsync sync)
{
glDeleteSyncAPPLE(sync);
}
GLenum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
{
return glClientWaitSyncAPPLE(sync, flags, timeout);
}
void glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
{
glWaitSyncAPPLE(sync, flags, timeout);
}
GLvoid glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
{
GLES3::glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
GLvoid glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
{
GLES3::glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
}
GLvoid glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments)
{
GLES3::glInvalidateFramebuffer(target, numAttachments, attachments);
}
namespace RBX
{
namespace Graphics
{
class ContextGLEAGL: public ContextGL
{
public:
ContextGLEAGL(void* windowHandle)
{
view = [(UIView*)windowHandle retain];
RBXASSERT(view);
RBXASSERT([view isKindOfClass:[UIView class]]);
layer = [(CAEAGLLayer*)view.layer retain];
RBXASSERT(layer);
RBXASSERT([layer isKindOfClass:[CAEAGLLayer class]]);
// Create context
if (FFlag::GraphicsGL3)
{
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
if (!context)
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!context)
throw std::runtime_error("Error creating context");
}
else
{
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!context)
throw std::runtime_error("Error creating context");
}
// Enable Retina if available
if (isMainFramebufferRetina())
view.contentScaleFactor = 2.0;
// Perform basic layer configuration
[layer setOpaque:YES];
[layer setDrawableProperties: [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]];
if (![EAGLContext setCurrentContext:context])
throw std::runtime_error("Error changing context");
// Create main color/depth buffer
glGenRenderbuffers(1, &colorRenderbufferId);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbufferId);
if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer])
throw std::runtime_error("Error allocating main renderbuffer");
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &renderbufferWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &renderbufferHeight);
FASTLOG4(FLog::Graphics, "Initialized EAGLContext %p (view %p) with renderbuffer %dx%d", context, view, renderbufferWidth, renderbufferHeight);
glGenRenderbuffers(1, &depthRenderbufferId);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbufferId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, renderbufferWidth, renderbufferHeight);
// Create main framebuffer
glGenFramebuffers(1, &framebufferId);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferId);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbufferId);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbufferId);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbufferId);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
throw RBX::runtime_error("Unsupported main framebuffer configuration: error %x", status);
}
~ContextGLEAGL()
{
setCurrent();
glDeleteFramebuffers(1, &framebufferId);
glDeleteRenderbuffers(1, &colorRenderbufferId);
glDeleteRenderbuffers(1, &depthRenderbufferId);
[EAGLContext setCurrentContext:nil];
[context release];
[layer release];
[view release];
}
virtual void setCurrent()
{
if ([EAGLContext currentContext] != context)
{
bool result = [EAGLContext setCurrentContext:context];
RBXASSERT(result);
}
}
virtual void swapBuffers()
{
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbufferId);
[context presentRenderbuffer:GL_RENDERBUFFER];
}
virtual unsigned int getMainFramebufferId()
{
return framebufferId;
}
virtual bool isMainFramebufferRetina()
{
// We can render with Retina on all devices that support Retina with iOS 6+ and GLES3
// iOS 5.1 has a non-conforming OpenGL ES implementation that does not allow us to render into NPOT targets
return
[[UIScreen mainScreen] scale] >= 2.0 &&
[[[UIDevice currentDevice] systemVersion] compare:@"6.0" options:NSNumericSearch] != NSOrderedAscending &&
context.API >= kEAGLRenderingAPIOpenGLES3;
}
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height)
{
return std::make_pair(renderbufferWidth, renderbufferHeight);
}
private:
UIView* view;
CAEAGLLayer* layer;
EAGLContext* context;
unsigned int colorRenderbufferId;
unsigned int depthRenderbufferId;
unsigned int framebufferId;
int renderbufferWidth;
int renderbufferHeight;
};
ContextGL* ContextGL::create(void* windowHandle)
{
return new ContextGLEAGL(windowHandle);
}
}
}
#endif
+477
View File
@@ -0,0 +1,477 @@
#include "DeviceGL.h"
#include "GeometryGL.h"
#include "ShaderGL.h"
#include "TextureGL.h"
#include "FramebufferGL.h"
#include "HeadersGL.h"
FASTFLAGVARIABLE(GraphicsGLUseDiscard, false)
namespace RBX
{
namespace Graphics
{
static const GLenum gCullModeGL[RasterizerState::Cull_Count] =
{
GL_NONE,
GL_BACK,
GL_FRONT
};
struct BlendFuncGL
{
GLenum src, dst;
};
static const GLenum gBlendFactorsGL[BlendState::Factor_Count] =
{
GL_ONE,
GL_ZERO,
GL_DST_COLOR,
GL_SRC_ALPHA,
GL_ONE_MINUS_SRC_ALPHA,
GL_DST_ALPHA,
GL_ONE_MINUS_DST_ALPHA
};
static const GLenum gDepthFuncGL[DepthState::Function_Count] =
{
GL_ALWAYS,
GL_LESS,
GL_LEQUAL
};
DeviceContextGL::DeviceContextGL(DeviceGL* dev)
: globalDataVersion(0)
, device(dev)
, defaultAnisotropy(1)
, cachedProgram(0)
, cachedRasterizerState(RasterizerState::Cull_None)
, cachedBlendState(BlendState::Mode_None)
, cachedDepthState(DepthState::Function_Always, false)
{
for (size_t i = 0; i < ARRAYSIZE(cachedTextures); ++i)
cachedTextures[i] = NULL;
}
DeviceContextGL::~DeviceContextGL()
{
}
void DeviceContextGL::clearStates()
{
// Clear framebuffer cache
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Clear program cache
cachedProgram = 0;
glUseProgram(0);
// Clear texture cache
for (size_t i = 0; i < ARRAYSIZE(cachedTextures); ++i)
cachedTextures[i] = NULL;
// Clear states to invalid values to guarantee a cache miss on the next setup
cachedRasterizerState = RasterizerState(RasterizerState::Cull_Count);
cachedBlendState = BlendState(BlendState::Mode_Count);
cachedDepthState = DepthState(DepthState::Function_Count, false);
// Setup state we never touch once
#ifndef GLES
glEnable(GL_PROGRAM_POINT_SIZE);
#endif
}
void DeviceContextGL::invalidateCachedProgram()
{
cachedProgram = 0;
}
void DeviceContextGL::invalidateCachedTexture(Texture* texture)
{
for (unsigned int stage = 0; stage < ARRAYSIZE(cachedTextures); ++stage)
if (cachedTextures[stage] == texture)
cachedTextures[stage] = NULL;
}
void DeviceContextGL::invalidateCachedTextureStage(unsigned int stage)
{
RBXASSERT(stage < ARRAYSIZE(cachedTextures));
cachedTextures[stage] = NULL;
}
void DeviceContextGL::defineGlobalConstants(size_t dataSize)
{
RBXASSERT(globalData.empty());
RBXASSERT(dataSize > 0);
globalData.resize(dataSize);
}
void DeviceContextGL::setDefaultAnisotropy(unsigned int value)
{
defaultAnisotropy = value;
}
void DeviceContextGL::updateGlobalConstants(const void* data, size_t dataSize)
{
RBXASSERT(dataSize == globalData.size());
memcpy(&globalData[0], data, dataSize);
globalDataVersion++;
}
void DeviceContextGL::bindFramebuffer(Framebuffer* buffer)
{
unsigned int drawId = static_cast<FramebufferGL*>(buffer)->getId();
glBindFramebuffer(GL_FRAMEBUFFER, drawId);
glViewport(0, 0, buffer->getWidth(), buffer->getHeight());
#ifndef GLES
unsigned int drawBuffers = static_cast<FramebufferGL*>(buffer)->getDrawBuffers();
if (drawId == 0)
{
glDrawBuffer(GL_BACK);
}
else if (glDrawBuffers)
{
GLenum buffers[16];
RBXASSERT(drawBuffers < ARRAYSIZE(buffers));
for (unsigned int i = 0; i < drawBuffers; ++i)
buffers[i] = GL_COLOR_ATTACHMENT0 + i;
glDrawBuffers(drawBuffers, buffers);
}
#endif
}
void DeviceContextGL::clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil)
{
unsigned int maskGl = 0;
if (mask & Buffer_Color)
{
// Need color writes for color clear to work
setBlendState(BlendState(BlendState::Mode_None, BlendState::Color_All));
maskGl |= GL_COLOR_BUFFER_BIT;
glClearColor(color[0], color[1], color[2], color[3]);
}
if (mask & Buffer_Depth)
{
// Need depth writes for depth clear to work
setDepthState(DepthState(DepthState::Function_Always, true));
maskGl |= GL_DEPTH_BUFFER_BIT;
glClearDepth(depth);
}
if (mask & Buffer_Stencil)
{
// Need stencil writes for stencil clear to work
glStencilMask(~0u);
maskGl |= GL_STENCIL_BUFFER_BIT;
glClearStencil(stencil);
}
RBXASSERT(maskGl);
glClear(maskGl);
}
void DeviceContextGL::copyFramebuffer(Framebuffer* buffer, Texture* texture)
{
RBXASSERT(texture->getType() == Texture::Type_2D);
RBXASSERT(buffer->getWidth() == texture->getWidth() && buffer->getHeight() == texture->getHeight());
invalidateCachedTextureStage(0);
GLint oldfb = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<FramebufferGL*>(buffer)->getId());
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, static_cast<TextureGL*>(texture)->getId());
#ifndef GLES
glReadBuffer(GL_COLOR_ATTACHMENT0);
#endif
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, texture->getWidth(), texture->getHeight());
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
}
void DeviceContextGL::resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask)
{
RBXASSERT(msaaBuffer->getSamples() > 1);
RBXASSERT(buffer->getSamples() == 1);
RBXASSERT(msaaBuffer->getWidth() == buffer->getWidth() && msaaBuffer->getHeight() == buffer->getHeight());
GLint oldfb = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
glBindFramebuffer(GL_READ_FRAMEBUFFER, static_cast<FramebufferGL*>(msaaBuffer)->getId());
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast<FramebufferGL*>(buffer)->getId());
if (mask & Buffer_Color)
{
#ifndef GLES
glReadBuffer(GL_COLOR_ATTACHMENT0);
#endif
glBlitFramebuffer(0, 0, buffer->getWidth(), buffer->getHeight(), 0, 0, buffer->getWidth(), buffer->getHeight(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
if (mask & (Buffer_Depth | Buffer_Stencil))
{
unsigned int maskGl = 0;
if (mask & Buffer_Depth)
maskGl |= GL_DEPTH_BUFFER_BIT;
if (mask & Buffer_Stencil)
maskGl |= GL_STENCIL_BUFFER_BIT;
glBlitFramebuffer(0, 0, buffer->getWidth(), buffer->getHeight(), 0, 0, buffer->getWidth(), buffer->getHeight(), maskGl, GL_NEAREST);
}
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
}
void DeviceContextGL::discardFramebuffer(Framebuffer* buffer, unsigned int mask)
{
if (!device->getCapsGL().ext3)
return;
if (!FFlag::GraphicsGLUseDiscard)
return;
unsigned int drawBuffers = static_cast<FramebufferGL*>(buffer)->getDrawBuffers();
GLenum attachments[16];
unsigned int index = 0;
if (mask & Buffer_Color)
for (unsigned int i = 0; i < drawBuffers; ++i)
attachments[index++] = GL_COLOR_ATTACHMENT0 + i;
if (mask & Buffer_Depth)
attachments[index++] = GL_DEPTH_ATTACHMENT;
if (mask & Buffer_Stencil)
attachments[index++] = GL_STENCIL_ATTACHMENT;
if (index > 0)
{
GLint oldfb = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<FramebufferGL*>(buffer)->getId());
glInvalidateFramebuffer(GL_FRAMEBUFFER, index, attachments);
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
}
}
void DeviceContextGL::bindProgram(ShaderProgram* program)
{
static_cast<ShaderProgramGL*>(program)->bind(&globalData[0], globalDataVersion, &cachedProgram);
}
void DeviceContextGL::setWorldTransforms4x3(const float* data, size_t matrixCount)
{
cachedProgram->setWorldTransforms4x3(data, matrixCount);
}
void DeviceContextGL::setConstant(int handle, const float* data, size_t vectorCount)
{
cachedProgram->setConstant(handle, data, vectorCount);
}
void DeviceContextGL::bindTexture(unsigned int stage, Texture* texture, const SamplerState& state)
{
SamplerState realState =
(state.getFilter() == SamplerState::Filter_Anisotropic && state.getAnisotropy() == 0)
? SamplerState(state.getFilter(), state.getAddress(), defaultAnisotropy)
: state;
RBXASSERT(stage < device->getCaps().maxTextureUnits);
RBXASSERT(stage < ARRAYSIZE(cachedTextures));
static_cast<TextureGL*>(texture)->bind(stage, realState, &cachedTextures[stage]);
}
void DeviceContextGL::setRasterizerState(const RasterizerState& state)
{
if (cachedRasterizerState != state)
{
cachedRasterizerState = state;
if (state.getCullMode() == RasterizerState::Cull_None)
{
glDisable(GL_CULL_FACE);
}
else
{
glEnable(GL_CULL_FACE);
glCullFace(gCullModeGL[state.getCullMode()]);
}
if (state.getDepthBias() == 0)
{
glDisable(GL_POLYGON_OFFSET_FILL);
}
else
{
float bias = static_cast<float>(state.getDepthBias());
float slopeBias = bias / 32.f; // do we need explicit control over slope or just a better formula since these numbers are magic anyway?
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(slopeBias, bias);
}
}
}
void DeviceContextGL::setBlendState(const BlendState& state)
{
if (cachedBlendState != state)
{
cachedBlendState = state;
unsigned int colorMask = state.getColorMask();
glColorMask((colorMask & BlendState::Color_R) != 0, (colorMask & BlendState::Color_G) != 0, (colorMask & BlendState::Color_B) != 0, (colorMask & BlendState::Color_A) != 0);
if (!state.blendingNeeded())
{
glDisable(GL_BLEND);
}
else
{
glEnable(GL_BLEND);
if (!state.separateAlphaBlend())
glBlendFunc(gBlendFactorsGL[state.getColorSrc()], gBlendFactorsGL[state.getColorDst()]);
else
glBlendFuncSeparate(gBlendFactorsGL[state.getColorSrc()], gBlendFactorsGL[state.getColorDst()], gBlendFactorsGL[state.getAlphaSrc()], gBlendFactorsGL[state.getAlphaDst()]);
}
}
}
void DeviceContextGL::setDepthState(const DepthState& state)
{
if (cachedDepthState != state)
{
cachedDepthState = state;
if (state.getFunction() == DepthState::Function_Always && !state.getWrite())
{
glDisable(GL_DEPTH_TEST);
}
else
{
glEnable(GL_DEPTH_TEST);
glDepthFunc(gDepthFuncGL[state.getFunction()]);
glDepthMask(state.getWrite());
}
switch (state.getStencilMode())
{
case DepthState::Stencil_None:
glDisable(GL_STENCIL_TEST);
break;
case DepthState::Stencil_IsNotZero:
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL, 0, ~0u);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
break;
case DepthState::Stencil_UpdateZFail:
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 0, ~0u);
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_DECR_WRAP, GL_KEEP);
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_INCR_WRAP, GL_KEEP);
break;
default:
RBXASSERT(false);
}
}
}
void DeviceContextGL::drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin, unsigned int indexRangeEnd)
{
static_cast<GeometryGL*>(geometry)->draw(primitive, offset, count);
}
//////////////////////////////////////////////////////////////////////////
// Markers:
void DeviceContextGL::pushDebugMarkerGroup(const char* text)
{
#ifdef RBX_PLATFORM_IOS
if (device->getCapsGL().extDebugMarkers)
{
glPushGroupMarkerEXT(0, text);
}
#elif defined(__ANDROID__)
;
#else
if (glPushDebugGroup) // Requires GL4.3, because ARB-version does not have marker enums for type
{
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION_ARB, 0, -1, text );
}
#endif
}
void DeviceContextGL::popDebugMarkerGroup()
{
#ifdef RBX_PLATFORM_IOS
if (device->getCapsGL().extDebugMarkers)
{
glPopGroupMarkerEXT();
}
#elif defined(__ANDROID__)
;
#else
if (glPopDebugGroup) // Requires GL4.3, because ARB-version does not have marker enums for type
{
glPopDebugGroup();
}
#endif
}
void DeviceContextGL::setDebugMarker(const char* text)
{
#ifdef RBX_PLATFORM_IOS
if (device->getCapsGL().extDebugMarkers)
{
glInsertEventMarkerEXT(0, text);
}
#elif defined(__ANDROID__)
;
#else
if (glDebugMessageInsert) // Requires GL4.3, because ARB-version does not have marker enums for type
{
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_MARKER, 0, GL_DEBUG_SEVERITY_NOTIFICATION, -1, text );
}
#endif
}
}
}
+636
View File
@@ -0,0 +1,636 @@
#include "DeviceGL.h"
#include "GeometryGL.h"
#include "ShaderGL.h"
#include "TextureGL.h"
#include "FramebufferGL.h"
#include "ContextGL.h"
#include "HeadersGL.h"
#include "rbx/Profiler.h"
#include <set>
#include <sstream>
LOGGROUP(Graphics)
FASTFLAGVARIABLE(DebugGraphicsGL, false)
FASTFLAGVARIABLE(GraphicsGL3, false)
FASTFLAG(RenderVR)
FASTFLAGVARIABLE(GraphicsGLReduceLatency, false)
namespace RBX
{
namespace Graphics
{
static std::set<std::string> getExtensions()
{
std::set<std::string> result;
if (const char* extensionString = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)))
{
std::istringstream ext(extensionString);
std::string str;
while (ext >> str)
result.insert(str);
return result;
}
#ifndef GLES
if (FFlag::GraphicsGL3 && GLEW_VERSION_3_0)
{
int extensionCount = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);
for (int i = 0; i < extensionCount; ++i)
result.insert(reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i)));
}
#endif
return result;
}
#ifdef GLES
static DeviceCapsGL createDeviceCaps(ContextGL* context, const std::set<std::string>& extensions)
{
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
bool version3 = FFlag::GraphicsGL3 && strncmp(version, "OpenGL ES ", 10) == 0 && version[10] >= '3';
DeviceCapsGL caps;
GLint texSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
GLint stencilBits;
glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
caps.supportsFramebuffer = true;
caps.supportsShaders = true;
caps.supportsFFP = false;
caps.supportsStencil = stencilBits >= 8;
caps.supportsIndex32 = version3 || extensions.count("GL_OES_element_index_uint");
caps.supportsTextureDXT = false;
caps.supportsTexturePVR = extensions.count("GL_IMG_texture_compression_pvrtc");
caps.supportsTextureHalfFloat = version3 || extensions.count("GL_OES_texture_half_float");
caps.supportsTexture3D = version3;
caps.supportsTextureNPOT = version3;
caps.supportsTextureETC1 = extensions.count("GL_OES_compressed_ETC1_RGB8_texture");
caps.supportsTexturePartialMipChain = version3 || extensions.count("GL_APPLE_texture_max_level") || (!FFlag::GraphicsGL3 && strstr(version, "OpenGL ES 3"));
caps.maxDrawBuffers = 1;
if (version3)
{
GLint samples = 1;
glGetIntegerv(GL_MAX_SAMPLES, &samples);
caps.maxSamples = samples;
}
else
{
caps.maxSamples = 1;
}
caps.maxTextureSize = texSize;
caps.maxTextureUnits = version3 ? 16 : 8;
caps.colorOrderBGR = false;
caps.needsHalfPixelOffset = false;
caps.requiresRenderTargetFlipping = true;
caps.retina = context->isMainFramebufferRetina();
caps.ext3 = version3;
caps.extVertexArrayObject = version3 || (extensions.count("GL_OES_vertex_array_object") && !strstr(renderer, "Adreno"));
caps.extTextureStorage = version3 || extensions.count("GL_EXT_texture_storage");
caps.extMapBuffer = version3 || extensions.count("GL_OES_mapbuffer");
caps.extMapBufferRange = version3 || extensions.count("GL_EXT_map_buffer_range");
caps.extTimerQuery = false;
caps.extDebugMarkers = extensions.count("GL_EXT_debug_marker");
caps.extSync = version3;
return caps;
}
#else
static DeviceCapsGL createDeviceCapsOld(ContextGL* context)
{
DeviceCapsGL caps;
GLint texSize = 0;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
GLint stencilBits = 0;
glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
caps.supportsFramebuffer = GLEW_VERSION_3_0 || GLEW_ARB_framebuffer_object || GLEW_EXT_framebuffer_object;
caps.supportsShaders = GLEW_VERSION_2_0 || (GLEW_ARB_shading_language_100 && GLEW_ARB_shader_objects && GLEW_ARB_fragment_shader && GLEW_ARB_vertex_shader);
caps.supportsFFP = false;
caps.supportsStencil = GLEW_VERSION_2_0 && stencilBits >= 8; // we need full two-sided stencil support
caps.supportsIndex32 = true;
caps.supportsTextureDXT = (GLEW_VERSION_1_3 || GLEW_ARB_texture_compression) && GLEW_EXT_texture_compression_s3tc;
caps.supportsTexturePVR = false;
caps.supportsTextureHalfFloat = !!GLEW_ARB_half_float_pixel;
caps.supportsTexture3D = GLEW_VERSION_1_2 || GLEW_EXT_texture3D;
caps.supportsTextureETC1 = false;
caps.supportsTexturePartialMipChain = true;
// According to http://aras-p.info/blog/2012/10/17/non-power-of-two-textures/, GL extensions lie
caps.supportsTextureNPOT = GLEW_ARB_texture_non_power_of_two && texSize >= 8192;
if (caps.supportsFramebuffer && (GLEW_VERSION_3_0 || GLEW_ARB_draw_buffers || GLEW_ATI_draw_buffers))
{
GLint drawBuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &drawBuffers);
caps.maxDrawBuffers = drawBuffers;
}
else
{
caps.maxDrawBuffers = 1;
}
if (caps.supportsFramebuffer && (GLEW_VERSION_3_3 || GLEW_EXT_framebuffer_multisample))
{
GLint samples = 0;
glGetIntegerv(GL_MAX_SAMPLES, &samples);
caps.maxSamples = samples;
}
else
{
caps.maxSamples = 1;
}
caps.maxTextureSize = texSize;
caps.maxTextureUnits = 16;
caps.colorOrderBGR = false;
caps.needsHalfPixelOffset = false;
caps.requiresRenderTargetFlipping = true;
caps.retina = context->isMainFramebufferRetina();
caps.ext3 = false;
caps.extVertexArrayObject = (GLEW_VERSION_3_0 || GLEW_ARB_vertex_array_object || GLEW_APPLE_vertex_array_object);
caps.extTextureStorage = (GLEW_VERSION_4_2 || GLEW_ARB_texture_storage);
caps.extMapBuffer = true;
caps.extMapBufferRange = (GLEW_VERSION_3_0 || GLEW_ARB_map_buffer_range);
caps.extTimerQuery = !!GLEW_EXT_timer_query;
caps.extDebugMarkers = false;
caps.extSync = (GLEW_VERSION_3_2 || GLEW_ARB_sync);
#ifdef _WIN32
const char* vendorString = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
if (strstr(vendorString, "Intel"))
{
// Intel drivers don't store IB state as part of VAO (http://stackoverflow.com/questions/8973690/vao-and-element-array-buffer-state)
caps.extVertexArrayObject = false;
}
if (strstr(vendorString, "ATI Technologies"))
{
// Some AMD drivers can't create cubemaps with TS and can create 3D textures that they can't update afterwards...
caps.extTextureStorage = false;
}
#endif
return caps;
}
static DeviceCapsGL createDeviceCaps(ContextGL* context, const std::set<std::string>& extensions)
{
if (!FFlag::GraphicsGL3)
return createDeviceCapsOld(context);
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
bool version3 = isdigit(version[0]) && version[0] >= '3';
DeviceCapsGL caps;
GLint texSize = 0;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
GLint stencilBits = 0;
glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
caps.supportsFramebuffer = version3 || (extensions.count("GL_ARB_framebuffer_object") || extensions.count("GL_EXT_framebuffer_object"));
caps.supportsShaders =
version3 || GLEW_VERSION_2_0 ||
// need this for Mac OpenGL 1.4 :-/
(extensions.count("GL_ARB_shading_language_100") && extensions.count("GL_ARB_shader_objects") && extensions.count("GL_ARB_fragment_shader") && extensions.count("GL_ARB_vertex_shader"));
caps.supportsFFP = false;
caps.supportsStencil = GLEW_VERSION_2_0 && stencilBits >= 8; // we need full two-sided stencil support
caps.supportsIndex32 = true;
caps.supportsTextureDXT = !!extensions.count("GL_EXT_texture_compression_s3tc");
caps.supportsTexturePVR = false;
caps.supportsTextureHalfFloat = version3;
caps.supportsTexture3D = true;
caps.supportsTextureETC1 = false;
caps.supportsTexturePartialMipChain = true;
caps.supportsTextureNPOT = version3;
if (version3)
{
GLint drawBuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &drawBuffers);
GLint samples = 0;
glGetIntegerv(GL_MAX_SAMPLES, &samples);
caps.maxDrawBuffers = drawBuffers;
caps.maxSamples = samples;
}
else
{
caps.maxDrawBuffers = 1;
caps.maxSamples = 1;
}
caps.maxTextureSize = texSize;
caps.maxTextureUnits = 16;
caps.colorOrderBGR = false;
caps.needsHalfPixelOffset = false;
caps.requiresRenderTargetFlipping = true;
caps.retina = context->isMainFramebufferRetina();
caps.ext3 = version3;
caps.extVertexArrayObject = version3;
caps.extTextureStorage = GLEW_VERSION_4_2 || (version3 && extensions.count("GL_ARB_texture_storage"));
caps.extMapBuffer = true;
caps.extMapBufferRange = version3;
caps.extTimerQuery = !!GLEW_VERSION_3_3;
caps.extDebugMarkers = false;
caps.extSync = !!GLEW_VERSION_3_2;
return caps;
}
static void GLAPIENTRY debugOutputGLARB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const GLvoid* userParam)
{
FASTLOGS(FLog::Graphics, "GL Debug: %s", message);
}
#endif
static DeviceVRGL* createVR()
{
#if defined(RBX_PLATFORM_IOS) || defined(__ANDROID__)
if (DeviceVRGL* result = DeviceVRGL::createCardboard())
return result;
#endif
return NULL;
}
DeviceGL::DeviceGL(void* windowHandle)
: frameTimeQueryId(0)
, frameTimeQueryIssued(false)
, frameEventQueryId(0)
, frameEventQueryIssued(false)
, gpuTime(0)
, vrEnabled(true)
{
glContext.reset(ContextGL::create(windowHandle));
if (FFlag::RenderVR)
vr.reset(createVR());
std::set<std::string> extensions = getExtensions();
caps = createDeviceCaps(glContext.get(), extensions);
FASTLOGS(FLog::Graphics, "GL Renderer: %s", reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
FASTLOGS(FLog::Graphics, "GL Version: %s", reinterpret_cast<const char*>(glGetString(GL_VERSION)));
FASTLOGS(FLog::Graphics, "GL Vendor: %s", reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
if (caps.supportsShaders)
FASTLOGS(FLog::Graphics, "GLSL version: %s", reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
std::string extensionString;
for (auto& e: extensions)
{
extensionString += " ";
extensionString += e;
}
while (!extensionString.empty())
{
std::string::size_type space = extensionString.find_first_of(' ', 128);
FASTLOGS(FLog::Graphics, "Ext:%s", extensionString.substr(0, space));
extensionString.erase(0, space);
}
caps.dumpToFLog(FLog::Graphics);
FASTLOG1(FLog::Graphics, "Caps: GL3 %d", caps.ext3);
FASTLOG5(FLog::Graphics, "Caps: VAO %d TexStorage %d MapBuffer %d MapBufferRange %d TimerQuery %d", caps.extVertexArrayObject, caps.extTextureStorage, caps.extMapBuffer, caps.extMapBufferRange, caps.extTimerQuery);
FASTLOG2(FLog::Graphics, "Caps: DebugMarkers %d Sync %d", caps.extDebugMarkers, caps.extSync);
immediateContext.reset(new DeviceContextGL(this));
mainFramebuffer.reset(new FramebufferGL(this, glContext->getMainFramebufferId()));
std::pair<unsigned int, unsigned int> dimensions = glContext->updateMainFramebuffer(0, 0);
mainFramebuffer->updateDimensions(dimensions.first, dimensions.second);
#ifndef GLES
if (caps.extTimerQuery)
{
glGenQueries(1, &frameTimeQueryId);
RBXASSERT(frameTimeQueryId);
}
if (FFlag::DebugGraphicsGL && GLEW_ARB_debug_output)
{
glDebugMessageCallbackARB(debugOutputGLARB, NULL);
}
#endif
#ifndef _WIN32
Profiler::gpuInit(0);
#endif
if (vr)
vr->setup(this);
}
DeviceGL::~DeviceGL()
{
#ifndef _WIN32
Profiler::gpuShutdown();
#endif
vr.reset();
immediateContext.reset();
mainFramebuffer.reset();
glContext.reset();
}
std::string DeviceGL::getFeatureLevel()
{
std::string glString = reinterpret_cast<const char*>(glGetString(GL_VERSION));
std::string featureLevel = "OpenGL ";
if (!glString.empty())
{
size_t dotId = glString.find('.');
// this is based on GLEW implementation of determining GL version. Does it look unsafe? Dont worry, GLEW version is even worse;)
if (dotId > 0 && dotId < (glString.length() -1) && isdigit(glString[dotId-1]) && isdigit(glString[dotId+1]))
{
featureLevel += glString[dotId - 1];
featureLevel += '.';
featureLevel += glString[dotId + 1];
return featureLevel;
}
}
featureLevel += "unknown";
return featureLevel;
}
bool DeviceGL::validate()
{
glContext->setCurrent();
std::pair<unsigned int, unsigned int> dimensions = glContext->updateMainFramebuffer(mainFramebuffer->getWidth(), mainFramebuffer->getHeight());
mainFramebuffer->updateDimensions(dimensions.first, dimensions.second);
return true;
}
DeviceContext* DeviceGL::beginFrame()
{
glContext->setCurrent();
#ifndef GLES
if (frameTimeQueryId && !frameTimeQueryIssued)
{
glBeginQuery(GL_TIME_ELAPSED, frameTimeQueryId);
}
#endif
immediateContext->bindFramebuffer(mainFramebuffer.get());
immediateContext->clearStates();
return immediateContext.get();
}
void DeviceGL::endFrame()
{
#ifndef GLES
if (frameTimeQueryId)
{
if (!frameTimeQueryIssued)
{
glEndQuery(GL_TIME_ELAPSED);
frameTimeQueryIssued = true;
}
else
{
int available = 0;
glGetQueryObjectiv(frameTimeQueryId, GL_QUERY_RESULT_AVAILABLE, &available);
if (available)
{
GLuint64EXT elapsed = 0;
if (FFlag::GraphicsGL3)
glGetQueryObjectui64v(frameTimeQueryId, GL_QUERY_RESULT, &elapsed);
else
glGetQueryObjectui64vEXT(frameTimeQueryId, GL_QUERY_RESULT, &elapsed);
gpuTime = static_cast<float>(elapsed) / 1e6f;
frameTimeQueryIssued = false;
}
}
}
#endif
if (FFlag::GraphicsGLReduceLatency && caps.extSync)
{
// Wait for last frame to finish on GPU
if (frameEventQueryIssued)
{
int rc = glClientWaitSync(frameEventQueryId, GL_SYNC_FLUSH_COMMANDS_BIT, GLuint64(5e9));
RBXASSERT(rc == GL_CONDITION_SATISFIED || rc == GL_ALREADY_SIGNALED);
glDeleteSync(frameEventQueryId);
frameEventQueryId = 0;
frameEventQueryIssued = false;
}
// Submit new query for frame wait
if (!frameEventQueryIssued)
{
frameEventQueryId = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
RBXASSERT(frameEventQueryId);
frameEventQueryIssued = true;
}
}
if (vr && vrEnabled)
{
vr->submitFrame(immediateContext.get());
}
glContext->swapBuffers();
}
Framebuffer* DeviceGL::getMainFramebuffer()
{
return mainFramebuffer.get();
}
DeviceVR* DeviceGL::getVR()
{
return (vr && vrEnabled) ? vr.get() : NULL;
}
void DeviceGL::setVR(bool enabled)
{
vrEnabled = enabled;
}
void DeviceGL::defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants)
{
RBXASSERT(globalConstants.empty());
RBXASSERT(!constants.empty());
globalConstants = constants;
immediateContext->defineGlobalConstants(dataSize);
}
std::string DeviceGL::getShadingLanguage()
{
#ifdef GLES
return caps.ext3 ? "glsles3" : "glsles";
#else
return caps.ext3 ? "glsl3" : "glsl";
#endif
}
std::string DeviceGL::createShaderSource(const std::string& path, const std::string& defines, boost::function<std::string (const std::string&)> fileCallback)
{
// No preprocessor support
return fileCallback(path);
}
std::vector<char> DeviceGL::createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint)
{
// No bytecode support
RBXASSERT(entrypoint == "main");
return std::vector<char>(source.begin(), source.end());
}
shared_ptr<VertexShader> DeviceGL::createVertexShader(const std::vector<char>& bytecode)
{
if (!caps.supportsShaders)
throw RBX::runtime_error("No shader support");
std::string source(bytecode.begin(), bytecode.end());
return shared_ptr<VertexShader>(new VertexShaderGL(this, source));
}
shared_ptr<FragmentShader> DeviceGL::createFragmentShader(const std::vector<char>& bytecode)
{
if (!caps.supportsShaders)
throw RBX::runtime_error("No shader support");
std::string source(bytecode.begin(), bytecode.end());
return shared_ptr<FragmentShader>(new FragmentShaderGL(this, source));
}
shared_ptr<ShaderProgram> DeviceGL::createShaderProgram(const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
{
if (!caps.supportsShaders)
throw RBX::runtime_error("No shader support");
return shared_ptr<ShaderProgram>(new ShaderProgramGL(this, vertexShader, fragmentShader));
}
shared_ptr<ShaderProgram> DeviceGL::createShaderProgramFFP()
{
throw RBX::runtime_error("No FFP support");
}
shared_ptr<VertexBuffer> DeviceGL::createVertexBuffer(size_t elementSize, size_t elementCount, VertexBuffer::Usage usage)
{
return shared_ptr<VertexBuffer>(new VertexBufferGL(this, elementSize, elementCount, usage));
}
shared_ptr<IndexBuffer> DeviceGL::createIndexBuffer(size_t elementSize, size_t elementCount, VertexBuffer::Usage usage)
{
return shared_ptr<IndexBuffer>(new IndexBufferGL(this, elementSize, elementCount, usage));
}
shared_ptr<VertexLayout> DeviceGL::createVertexLayout(const std::vector<VertexLayout::Element>& elements)
{
return shared_ptr<VertexLayout>(new VertexLayoutGL(this, elements));
}
shared_ptr<Texture> DeviceGL::createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Texture::Usage usage)
{
return shared_ptr<Texture>(new TextureGL(this, type, format, width, height, depth, mipLevels, usage));
}
shared_ptr<Renderbuffer> DeviceGL::createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
{
return shared_ptr<Renderbuffer>(new RenderbufferGL(this, format, width, height, samples));
}
shared_ptr<Geometry> DeviceGL::createGeometryImpl(const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer> >& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
{
return shared_ptr<Geometry>(new GeometryGL(this, layout, vertexBuffers, indexBuffer, baseVertexIndex));
}
shared_ptr<Framebuffer> DeviceGL::createFramebufferImpl(const std::vector<shared_ptr<Renderbuffer> >& color, const shared_ptr<Renderbuffer>& depth)
{
return shared_ptr<Framebuffer>(new FramebufferGL(this, color, depth));
}
DeviceStats DeviceGL::getStatistics() const
{
DeviceStats result = {};
result.gpuFrameTime = gpuTime;
return result;
}
}
}
+172
View File
@@ -0,0 +1,172 @@
#pragma once
#include "GfxCore/Device.h"
#include "GfxCore/States.h"
typedef struct __GLsync *GLsync;
namespace RBX
{
namespace Graphics
{
class FramebufferGL;
class ShaderProgramGL;
class TextureGL;
class ContextGL;
class DeviceGL;
class DeviceVRGL: public DeviceVR
{
public:
virtual void setup(Device* device) = 0;
virtual void submitFrame(DeviceContext* context) = 0;
static DeviceVRGL* createCardboard();
};
struct DeviceCapsGL: DeviceCaps
{
bool ext3;
bool extVertexArrayObject;
bool extTextureStorage;
bool extMapBuffer;
bool extMapBufferRange;
bool extTimerQuery;
bool extDebugMarkers;
bool extSync;
};
class DeviceContextGL: public DeviceContext
{
public:
DeviceContextGL(DeviceGL* dev);
~DeviceContextGL();
void defineGlobalConstants(size_t dataSize);
void clearStates();
void invalidateCachedProgram();
void invalidateCachedTexture(Texture* texture);
void invalidateCachedTextureStage(unsigned int stage);
virtual void setDefaultAnisotropy(unsigned int value);
virtual void updateGlobalConstants(const void* data, size_t dataSize);
virtual void bindFramebuffer(Framebuffer* buffer);
virtual void clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil);
virtual void copyFramebuffer(Framebuffer* buffer, Texture* texture);
virtual void resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask);
virtual void discardFramebuffer(Framebuffer* buffer, unsigned int mask);
virtual void bindProgram(ShaderProgram* program);
virtual void setWorldTransforms4x3(const float* data, size_t matrixCount);
virtual void setConstant(int handle, const float* data, size_t vectorCount);
virtual void bindTexture(unsigned int stage, Texture* texture, const SamplerState& state);
virtual void setRasterizerState(const RasterizerState& state);
virtual void setBlendState(const BlendState& state);
virtual void setDepthState(const DepthState& state);
virtual void drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin, unsigned int indexRangeEnd);
virtual void pushDebugMarkerGroup(const char* text);
virtual void popDebugMarkerGroup();
virtual void setDebugMarker(const char* text);
private:
std::vector<char> globalData;
unsigned int globalDataVersion;
unsigned int defaultAnisotropy;
ShaderProgramGL* cachedProgram;
TextureGL* cachedTextures[16];
RasterizerState cachedRasterizerState;
BlendState cachedBlendState;
DepthState cachedDepthState;
DeviceGL* device;
};
class DeviceGL: public Device
{
public:
DeviceGL(void* windowHandle);
~DeviceGL();
virtual bool validate();
virtual DeviceContext* beginFrame();
virtual void endFrame();
virtual Framebuffer* getMainFramebuffer();
virtual DeviceVR* getVR();
virtual void setVR(bool enabled);
virtual void defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants);
virtual std::string getAPIName() { return "OpenGL"; }
virtual std::string getFeatureLevel();
virtual std::string getShadingLanguage();
virtual std::string createShaderSource(const std::string& path, const std::string& defines, boost::function<std::string (const std::string&)> fileCallback);
virtual std::vector<char> createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint);
virtual shared_ptr<VertexShader> createVertexShader(const std::vector<char>& bytecode);
virtual shared_ptr<FragmentShader> createFragmentShader(const std::vector<char>& bytecode);
virtual shared_ptr<ShaderProgram> createShaderProgram(const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
virtual shared_ptr<ShaderProgram> createShaderProgramFFP();
virtual shared_ptr<VertexBuffer> createVertexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage);
virtual shared_ptr<IndexBuffer> createIndexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage);
virtual shared_ptr<VertexLayout> createVertexLayout(const std::vector<VertexLayout::Element>& elements);
virtual shared_ptr<Texture> createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Texture::Usage usage);
virtual shared_ptr<Renderbuffer> createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
virtual shared_ptr<Geometry> createGeometryImpl(const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer> >& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
virtual shared_ptr<Framebuffer> createFramebufferImpl(const std::vector<shared_ptr<Renderbuffer> >& color, const shared_ptr<Renderbuffer>& depth);
virtual const DeviceCaps& getCaps() const { return caps; }
virtual DeviceStats getStatistics() const;
DeviceContextGL* getImmediateContextGL() { return immediateContext.get(); }
const DeviceCapsGL& getCapsGL() const { return caps; }
const std::vector<ShaderGlobalConstant>& getGlobalConstants() const { return globalConstants; }
private:
DeviceCapsGL caps;
scoped_ptr<DeviceContextGL> immediateContext;
scoped_ptr<FramebufferGL> mainFramebuffer;
std::vector<ShaderGlobalConstant> globalConstants;
unsigned int frameTimeQueryId;
bool frameTimeQueryIssued;
GLsync frameEventQueryId;
bool frameEventQueryIssued;
float gpuTime;
scoped_ptr<DeviceVRGL> vr;
bool vrEnabled;
scoped_ptr<ContextGL> glContext;
};
}
}
+163
View File
@@ -0,0 +1,163 @@
#include "FramebufferGL.h"
#include "TextureGL.h"
#include "DeviceGL.h"
#include "HeadersGL.h"
namespace RBX
{
namespace Graphics
{
RenderbufferGL::RenderbufferGL(Device* device, const shared_ptr<TextureGL>& container, unsigned int target)
: Renderbuffer(device, container->getFormat(), container->getWidth(), container->getHeight(), 1)
, owner(container)
, target(target)
, bufferId(0)
{
}
RenderbufferGL::RenderbufferGL(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
: Renderbuffer(device, format, width, height, samples)
, target(-1)
, bufferId(0)
{
RBXASSERT(samples);
if (samples > device->getCaps().maxSamples)
throw RBX::runtime_error("Unsupported renderbuffer: too many samples (%d)", samples);
glGenRenderbuffers(1, &bufferId);
glBindRenderbuffer(GL_RENDERBUFFER, bufferId);
if (samples > 1)
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, TextureGL::getInternalFormat(format), width, height);
else
glRenderbufferStorage(GL_RENDERBUFFER, TextureGL::getInternalFormat(format), width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
RenderbufferGL::~RenderbufferGL()
{
if (bufferId)
glDeleteRenderbuffers(1, &bufferId);
}
FramebufferGL::FramebufferGL(Device* device, unsigned int id)
: Framebuffer(device, 0, 0, 1)
, id(id)
{
}
FramebufferGL::FramebufferGL(Device* device, const std::vector<shared_ptr<Renderbuffer> >& color, const shared_ptr<Renderbuffer>& depth)
: Framebuffer(device, 0, 0, 0)
, id(0)
, color(color)
, depth(depth)
{
RBXASSERT(!color.empty());
if (color.size() > device->getCaps().maxDrawBuffers)
throw RBX::runtime_error("Unsupported framebuffer configuration: too many buffers (%d)", (int)color.size());
glGenFramebuffers(1, &id);
glBindFramebuffer(GL_FRAMEBUFFER, id);
for (size_t i = 0; i < color.size(); ++i)
{
RenderbufferGL* buffer = static_cast<RenderbufferGL*>(color[i].get());
RBXASSERT(buffer);
RBXASSERT(!Texture::isFormatDepth(buffer->getFormat()));
if (buffer->getTextureId())
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, buffer->getTarget(), buffer->getTextureId(), 0);
else
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_RENDERBUFFER, buffer->getBufferId());
if (i == 0)
{
width = buffer->getWidth();
height = buffer->getHeight();
samples = buffer->getSamples();
}
else
{
RBXASSERT(width == buffer->getWidth());
RBXASSERT(height == buffer->getHeight());
RBXASSERT(samples == buffer->getSamples());
}
}
if (depth)
{
RenderbufferGL* buffer = static_cast<RenderbufferGL*>(depth.get());
RBXASSERT(buffer->getTextureId() == 0);
RBXASSERT(Texture::isFormatDepth(buffer->getFormat()));
RBXASSERT(width == buffer->getWidth());
RBXASSERT(height == buffer->getHeight());
RBXASSERT(samples == buffer->getSamples());
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, buffer->getBufferId());
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer->getBufferId());
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
glDeleteFramebuffers(1, &id);
throw RBX::runtime_error("Unsupported framebuffer configuration: error %x", status);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
FramebufferGL::~FramebufferGL()
{
if (id)
glDeleteFramebuffers(1, &id);
}
void FramebufferGL::download(void* data, unsigned int size)
{
RBXASSERT(size == width * height * 4);
GLint oldfb = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
glBindFramebuffer(GL_FRAMEBUFFER, id);
#ifndef GLES
glReadBuffer(id == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0);
#endif
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
std::vector<char> tempRow(width * 4);
for (unsigned int y = 0; y < height / 2; ++y)
{
char* dataRow = static_cast<char*>(data) + y * width * 4;
char* dataOppositeRow = static_cast<char*>(data) + (height - 1 - y) * width * 4;
memcpy(&tempRow[0], dataRow, width * 4);
memcpy(dataRow, dataOppositeRow, width * 4);
memcpy(dataOppositeRow, &tempRow[0], width * 4);
}
}
void FramebufferGL::updateDimensions(unsigned int width, unsigned int height)
{
RBXASSERT(color.empty() && !depth);
this->width = width;
this->height = height;
}
}
}
+52
View File
@@ -0,0 +1,52 @@
#pragma once
#include "GfxCore/Framebuffer.h"
#include "TextureGL.h"
#include <vector>
namespace RBX
{
namespace Graphics
{
class RenderbufferGL: public Renderbuffer
{
public:
RenderbufferGL(Device* device, const shared_ptr<TextureGL>& owner, unsigned int target);
RenderbufferGL(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
~RenderbufferGL();
unsigned int getTextureId() const { return owner ? owner->getId() : 0; }
unsigned int getTarget() const { return target; }
unsigned int getBufferId() const { return bufferId; }
private:
unsigned int target; // TEXTURE_2D or TEXTURE_CUBE_MAP_POSITIVE_X, ...
unsigned int bufferId;
shared_ptr<TextureGL> owner;
};
class FramebufferGL: public Framebuffer
{
public:
FramebufferGL(Device* device, unsigned int id);
FramebufferGL(Device* device, const std::vector<shared_ptr<Renderbuffer> >& color, const shared_ptr<Renderbuffer>& depth);
~FramebufferGL();
virtual void download(void* data, unsigned int size);
void updateDimensions(unsigned int width, unsigned int height);
unsigned int getId() const { return id; }
unsigned int getDrawBuffers() const { return color.size(); }
private:
unsigned int id;
std::vector<shared_ptr<Renderbuffer> > color;
shared_ptr<Renderbuffer> depth;
};
}
}
+229
View File
@@ -0,0 +1,229 @@
#if defined(__ANDROID__)
#include "DeviceGL.h"
#include "FramebufferGL.h"
#include "TextureGL.h"
#include <EGL/egl.h>
#include "VrApi.h"
#include "VrApi_Helpers.h"
LOGGROUP(VR)
FASTFLAGVARIABLE(GearVR, false)
namespace RBX
{
namespace Graphics
{
static ovrJava gJava;
static DeviceVR::Pose getPose(const ovrPosef& pose, unsigned int statusFlags)
{
DeviceVR::Pose result = {};
result.valid = (statusFlags & VRAPI_TRACKING_STATUS_ORIENTATION_TRACKED) != 0;
result.position[0] = pose.Position.x;
result.position[1] = pose.Position.y;
result.position[2] = pose.Position.z;
result.orientation[0] = pose.Orientation.x;
result.orientation[1] = pose.Orientation.y;
result.orientation[2] = pose.Orientation.z;
result.orientation[3] = pose.Orientation.w;
return result;
}
struct VRTexture
{
static const int kMaxCount = 4;
shared_ptr<Framebuffer> fb[kMaxCount];
ovrTextureSwapChain* swapChain;
unsigned int swapChainIndex;
VRTexture(): swapChain(NULL), swapChainIndex(0)
{
}
};
struct GearVRGL: DeviceVRGL
{
ovrMobile* session;
float eyeFovX, eyeFovY;
VRTexture textures[2];
long long frameIndex;
double sensorSampleTime;
ovrTracking trackingState;
GearVRGL(): session(NULL), frameIndex(0), sensorSampleTime(0)
{
}
~GearVRGL()
{
vrapi_LeaveVrMode(session);
for (int eye = 0; eye < 2; ++eye)
vrapi_DestroyTextureSwapChain(textures[eye].swapChain);
vrapi_Shutdown();
}
void update() override
{
frameIndex++;
double frameTime = vrapi_GetPredictedDisplayTime(session, frameIndex);
sensorSampleTime = vrapi_GetTimeInSeconds();
ovrTracking baseState = vrapi_GetPredictedTracking(session, frameTime);
ovrHeadModelParms headModel = vrapi_DefaultHeadModelParms();
trackingState = vrapi_ApplyHeadModel(&headModel, &baseState);
}
void recenter() override
{
vrapi_RecenterPose(session);
}
Framebuffer* getEyeFramebuffer(int eye) override
{
RBXASSERT(eye == 0 || eye == 1);
return textures[eye].fb[textures[eye].swapChainIndex].get();
}
State getState() override
{
State result = {};
result.headPose = getPose(trackingState.HeadPose.Pose, trackingState.Status);
ovrHeadModelParms headModel = vrapi_DefaultHeadModelParms();
float tanX = tanf(eyeFovX / 2 * (3.1415926f / 180));
float tanY = tanf(eyeFovY / 2 * (3.1415926f / 180));
for (int eye = 0; eye < 2; ++eye)
{
float eyeSign = (eye == 0 ? -1 : 1);
result.eyeOffset[eye][0] = eyeSign * headModel.InterpupillaryDistance / 2;
result.eyeOffset[eye][1] = 0;
result.eyeOffset[eye][2] = 0;
result.eyeFov[eye][0] = tanY;
result.eyeFov[eye][1] = tanY;
result.eyeFov[eye][2] = tanX;
result.eyeFov[eye][3] = tanX;
}
result.needsMirror = false;
return result;
}
void submitFrame(DeviceContext* context) override
{
double currentTime = vrapi_GetTimeInSeconds();
ovrMatrix4f eyeProjectionMatrix = ovrMatrix4f_CreateProjectionFov(eyeFovX, eyeFovY, 0, 0, VRAPI_ZNEAR, 0);
ovrMatrix4f tanAngleMatrix = ovrMatrix4f_TanAngleMatrixFromProjection(&eyeProjectionMatrix);
ovrFrameParms params = vrapi_DefaultFrameParms(&gJava, VRAPI_FRAME_INIT_DEFAULT, currentTime, NULL);
params.FrameIndex = frameIndex;
for (int eye = 0; eye < 2; ++eye)
{
params.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].ColorTextureSwapChain = textures[eye].swapChain;
params.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].TextureSwapChainIndex = textures[eye].swapChainIndex;
params.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].TexCoordsFromTanAngles = tanAngleMatrix;
params.Layers[VRAPI_FRAME_LAYER_TYPE_WORLD].Textures[eye].HeadPose = trackingState.HeadPose;
}
vrapi_SubmitFrame(session, &params);
for (int eye = 0; eye < 2; ++eye)
{
textures[eye].swapChainIndex++;
textures[eye].swapChainIndex %= vrapi_GetTextureSwapChainLength(textures[eye].swapChain);
}
}
void setup(Device* device) override
{
unsigned int width = vrapi_GetSystemPropertyInt(&gJava, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH);
unsigned int height = vrapi_GetSystemPropertyInt(&gJava, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_HEIGHT);
shared_ptr<Renderbuffer> depthStencil = device->createRenderbuffer(Texture::Format_D24S8, width, height, 1);
for (int eye = 0; eye < 2; ++eye)
{
textures[eye].swapChain = vrapi_CreateTextureSwapChain(VRAPI_TEXTURE_TYPE_2D, VRAPI_TEXTURE_FORMAT_8888, width, height, 1, true);
int textureCount = vrapi_GetTextureSwapChainLength(textures[eye].swapChain);
RBXASSERT(textureCount <= VRTexture::kMaxCount);
for (int i = 0; i < textureCount; ++i)
{
unsigned int textureId = vrapi_GetTextureSwapChainHandle(textures[eye].swapChain, i);
shared_ptr<Texture> texture(new TextureGL(device, Texture::Type_2D, Texture::Format_RGBA8, width, height, 1, 1, Texture::Usage_Renderbuffer, textureId));
textures[eye].fb[i] = device->createFramebuffer(texture->getRenderbuffer(0, 0), depthStencil);
}
}
}
};
DeviceVRGL* DeviceVRGL::createGear()
{
if (!FFlag::GearVR)
return NULL;
ovrInitParms initParms = vrapi_DefaultInitParms(&gJava);
ovrInitializeStatus status = vrapi_Initialize(&initParms);
if (status != VRAPI_INITIALIZE_SUCCESS)
{
FASTLOG1(FLog::VR, "VR: vrapi_Initialize returned %d", status);
return NULL;
}
ovrModeParms modeParms = vrapi_DefaultModeParms(&gJava);
ovrMobile* session = vrapi_EnterVrMode(&modeParms);
GearVRGL* vr = new GearVRGL();
vr->session = session;
vr->eyeFovX = vrapi_GetSystemPropertyFloat(&gJava, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X);
vr->eyeFovY = vrapi_GetSystemPropertyFloat(&gJava, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_Y);
return vr;
}
}
}
void vrGearSetJava(JavaVM* vm, JNIEnv* env, jobject activity)
{
using RBX::Graphics::gJava;
gJava.Vm = vm;
gJava.Env = env;
gJava.ActivityObject = activity;
}
#endif
+345
View File
@@ -0,0 +1,345 @@
#include "GeometryGL.h"
#include "DeviceGL.h"
#include "HeadersGL.h"
namespace RBX
{
namespace Graphics
{
static const GLenum gBufferUsageGL[GeometryBuffer::Usage_Count] =
{
GL_STATIC_DRAW,
GL_DYNAMIC_DRAW
};
static const GLenum gBufferLockGL[GeometryBuffer::Lock_Count] =
{
GL_MAP_WRITE_BIT,
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT
};
struct VertexFormatGL
{
int size;
GLenum type;
bool normalized;
};
static const VertexFormatGL gVertexFormatGL[VertexLayout::Format_Count] =
{
{ 1, GL_FLOAT, false },
{ 2, GL_FLOAT, false },
{ 3, GL_FLOAT, false },
{ 4, GL_FLOAT, false },
{ 2, GL_SHORT, false },
{ 4, GL_SHORT, false },
{ 4, GL_UNSIGNED_BYTE, false },
{ 4, GL_UNSIGNED_BYTE, true },
};
static const GLenum gGeometryPrimitiveGL[Geometry::Primitive_Count] =
{
GL_TRIANGLES,
GL_LINES,
GL_POINTS,
GL_TRIANGLE_STRIP
};
static unsigned int getVertexAttributeGL(VertexLayout::Semantic semantic, unsigned int index)
{
switch (semantic)
{
case VertexLayout::Semantic_Position:
RBXASSERT(index == 0);
return 0;
case VertexLayout::Semantic_Normal:
RBXASSERT(index == 0);
return 1;
case VertexLayout::Semantic_Color:
RBXASSERT(index < 2);
return 2 + index;
case VertexLayout::Semantic_Texture:
RBXASSERT(index < 8);
return 4 + index;
default:
RBXASSERT(false);
return -1;
}
}
const char* VertexLayoutGL::getShaderAttributeName(unsigned int id)
{
static const char* table[] =
{
"vertex",
"normal",
"colour",
"secondary_colour",
"uv0",
"uv1",
"uv2",
"uv3",
"uv4",
"uv5",
"uv6",
"uv7",
};
return (id < ARRAYSIZE(table)) ? table[id] : NULL;
}
VertexLayoutGL::VertexLayoutGL(Device* device, const std::vector<Element>& elements)
: VertexLayout(device, elements)
{
}
VertexLayoutGL::~VertexLayoutGL()
{
}
template <typename Base> GeometryBufferGL<Base>::GeometryBufferGL(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, unsigned int target)
: Base(device, elementSize, elementCount, usage)
, target(target)
, id(0)
, locked(0)
{
}
template <typename Base> void GeometryBufferGL<Base>::create()
{
glGenBuffers(1, &id);
RBXASSERT(id);
glBindBuffer(target, id);
glBufferData(target, this->elementSize * this->elementCount, NULL, gBufferUsageGL[this->usage]);
}
template <typename Base> GeometryBufferGL<Base>::~GeometryBufferGL()
{
RBXASSERT(!locked);
glDeleteBuffers(1, &id);
}
template <typename Base> void* GeometryBufferGL<Base>::lock(GeometryBuffer::LockMode mode)
{
RBXASSERT(!locked);
unsigned int size = this->elementSize * this->elementCount;
const DeviceCapsGL& caps = static_cast<DeviceGL*>(this->device)->getCapsGL();
if (caps.extMapBuffer)
{
glBindBuffer(target, id);
if (caps.extMapBufferRange)
{
locked = glMapBufferRange(target, 0, size, gBufferLockGL[mode]);
}
else
{
if (mode == GeometryBuffer::Lock_Discard)
glBufferData(target, size, NULL, gBufferUsageGL[this->usage]);
locked = glMapBuffer(target, GL_WRITE_ONLY);
}
}
else
{
locked = new char[size];
}
RBXASSERT(locked);
return locked;
}
template <typename Base> void GeometryBufferGL<Base>::unlock()
{
RBXASSERT(locked);
const DeviceCapsGL& caps = static_cast<DeviceGL*>(this->device)->getCapsGL();
if (caps.extMapBuffer)
{
glBindBuffer(target, id);
glUnmapBuffer(target);
}
else
{
glBindBuffer(target, id);
glBufferSubData(target, 0, this->elementSize * this->elementCount, locked);
delete[] static_cast<char*>(locked);
}
locked = NULL;
}
template <typename Base> void GeometryBufferGL<Base>::upload(unsigned int offset, const void* data, unsigned int size)
{
RBXASSERT(!locked);
RBXASSERT(offset + size <= this->elementSize * this->elementCount);
glBindBuffer(target, id);
glBufferSubData(target, offset, size, data);
}
VertexBufferGL::VertexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage)
: GeometryBufferGL<VertexBuffer>(device, elementSize, elementCount, usage, GL_ARRAY_BUFFER)
{
create();
}
VertexBufferGL::~VertexBufferGL()
{
}
IndexBufferGL::IndexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage)
: GeometryBufferGL<IndexBuffer>(device, elementSize, elementCount, usage, GL_ELEMENT_ARRAY_BUFFER)
{
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
if (elementSize != 2 && elementSize != 4)
throw RBX::runtime_error("Invalid element size: %d", (int)elementSize);
if (elementSize == 4 && !caps.supportsIndex32)
throw RBX::runtime_error("Unsupported element size: %d", (int)elementSize);
create();
}
IndexBufferGL::~IndexBufferGL()
{
}
GeometryGL::GeometryGL(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer> >& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
: Geometry(device, layout, vertexBuffers, indexBuffer, baseVertexIndex)
, id(0)
, indexElementSize(0)
{
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
if (caps.extVertexArrayObject)
{
glGenVertexArrays(1, &id);
RBXASSERT(id);
glBindVertexArray(id);
bindArrays();
glBindVertexArray(0);
}
if (indexBuffer)
{
// Cache indexElementSize to reduce cache misses in case we're using VAO
indexElementSize = indexBuffer->getElementSize();
}
}
GeometryGL::~GeometryGL()
{
if (id)
{
glDeleteVertexArrays(1, &id);
}
}
void GeometryGL::draw(Primitive primitive, unsigned int offset, unsigned int count)
{
unsigned int mask = 0;
// Setup vertex/index buffers
if (id)
glBindVertexArray(id);
else
mask = bindArrays();
// Perform draw call
if (indexBuffer)
{
GLenum indexElementType = indexElementSize == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
glDrawElements(gGeometryPrimitiveGL[primitive], count, indexElementType, reinterpret_cast<void*>(offset * indexElementSize));
}
else
glDrawArrays(gGeometryPrimitiveGL[primitive], offset, count);
// Unbind buffers to prevent draw call interference
if (id)
glBindVertexArray(0);
else
unbindArrays(mask);
}
unsigned int GeometryGL::bindArrays()
{
unsigned int mask = 0;
unsigned int vbId = 0;
const std::vector<VertexLayout::Element>& elements = layout->getElements();
for (size_t i = 0; i < elements.size(); ++i)
{
const VertexLayout::Element& e = elements[i];
int index = getVertexAttributeGL(e.semantic, e.semanticIndex);
RBXASSERT(index >= 0);
RBXASSERT(e.stream < vertexBuffers.size());
VertexBufferGL* vb = static_cast<VertexBufferGL*>(vertexBuffers[e.stream].get());
RBXASSERT(e.offset < vb->getElementSize());
size_t stride = vb->getElementSize();
size_t offset = e.offset + stride * baseVertexIndex;
const VertexFormatGL& formatGl = gVertexFormatGL[e.format];
if (vbId != vb->getId())
{
vbId = vb->getId();
glBindBuffer(GL_ARRAY_BUFFER, vbId);
}
glEnableVertexAttribArray(index);
glVertexAttribPointer(index, formatGl.size, formatGl.type, formatGl.normalized, stride, reinterpret_cast<void*>(offset));
mask |= 1 << index;
}
if (indexBuffer)
{
IndexBufferGL* ib = static_cast<IndexBufferGL*>(indexBuffer.get());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->getId());
}
return mask;
}
void GeometryGL::unbindArrays(unsigned int mask)
{
for (int i = 0; i < 16; ++i)
if (mask & (1 << i))
glDisableVertexAttribArray(i);
}
// Instantiate GeometryBufferGL template
template class GeometryBufferGL<VertexBuffer>;
template class GeometryBufferGL<IndexBuffer>;
}
}
+75
View File
@@ -0,0 +1,75 @@
#pragma once
#include "GfxCore/Geometry.h"
namespace RBX
{
namespace Graphics
{
class VertexLayoutGL: public VertexLayout
{
public:
static const char* getShaderAttributeName(unsigned int id);
VertexLayoutGL(Device* device, const std::vector<Element>& elements);
~VertexLayoutGL();
};
template <typename Base> class GeometryBufferGL: public Base
{
public:
GeometryBufferGL(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, unsigned int target);
~GeometryBufferGL();
virtual void* lock(GeometryBuffer::LockMode mode);
virtual void unlock();
virtual void upload(unsigned int offset, const void* data, unsigned int size);
unsigned int getId() const { return id; }
protected:
void create();
private:
unsigned int target;
unsigned int id;
void* locked;
};
class VertexBufferGL: public GeometryBufferGL<VertexBuffer>
{
public:
VertexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage);
~VertexBufferGL();
};
class IndexBufferGL: public GeometryBufferGL<IndexBuffer>
{
public:
IndexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage);
~IndexBufferGL();
};
class GeometryGL: public Geometry
{
public:
GeometryGL(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer> >& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
~GeometryGL();
void draw(Primitive primitive, unsigned int offset, unsigned int count);
unsigned int getId() const { return id; }
private:
unsigned int id;
unsigned int indexElementSize;
unsigned int bindArrays();
void unbindArrays(unsigned int mask);
};
}
}
+136
View File
@@ -0,0 +1,136 @@
#pragma once
#ifdef RBX_PLATFORM_IOS
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#ifdef __OBJC__
#include <OpenGLES/EAGL.h>
#endif
#define GLES
#elif defined(__ANDROID__)
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#define GLES
#else
#include <GL/glew.h>
void glewInitRBX();
#endif
#ifdef __ANDROID__
typedef struct __GLsync *GLsync;
typedef uint64_t GLuint64;
#endif
#ifdef GLES
// Core GLES
#define glClearDepth glClearDepthf
// OES_vertex_array_object
GLvoid glBindVertexArray(GLuint array);
GLvoid glDeleteVertexArrays(GLsizei n, const GLuint *arrays);
GLvoid glGenVertexArrays(GLsizei n, GLuint *arrays);
// OES_mapbuffer
#define GL_WRITE_ONLY 0x88B9
GLvoid *glMapBuffer(GLenum target, GLenum access);
GLboolean glUnmapBuffer(GLenum target);
// EXT_map_buffer_range
GLvoid *glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
#define GL_MAP_READ_BIT 0x0001
#define GL_MAP_WRITE_BIT 0x0002
#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004
#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010
#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020
// EXT_texture_storage
GLvoid glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
#define GL_LUMINANCE8 0x8040
#define GL_LUMINANCE8_ALPHA8 0x8045
// GL_OES_packed_depth_stencil
#define GL_DEPTH_STENCIL 0x84F9
#define GL_UNSIGNED_INT_24_8 0x84FA
#define GL_DEPTH24_STENCIL8 0x88F0
// OES_rgb8_rgba8
#define GL_RGBA8 0x8058
// OES_texture_half_float
#define GL_HALF_FLOAT 0x8D61
// EXT_color_buffer_half_float
#define GL_RGBA16F 0x881A
// OES_texture_3D
#define GL_TEXTURE_3D 0x806F
GLvoid glTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
GLvoid glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
GLvoid glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
// GLES 3.0 formats
#define GL_RED 0x1903
#define GL_RG 0x8227
#define GL_R8 0x8229
#define GL_RG8 0x822B
#define GL_RG16 0x822C
// GLES 3.0 buffer objects
#define GL_DYNAMIC_COPY 0x88EA
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
// GLES 3.0 sync objects
#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
#define GL_ALREADY_SIGNALED 0x911A
#define GL_TIMEOUT_EXPIRED 0x911B
#define GL_CONDITION_SATISFIED 0x911C
#define GL_WAIT_FAILED 0x911D
#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
GLsync glFenceSync(GLenum condition, GLbitfield flags);
void glDeleteSync(GLsync sync);
GLenum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
void glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
// GLES 3.0 MSAA
#define GL_READ_FRAMEBUFFER 0x8CA8
#define GL_DRAW_FRAMEBUFFER 0x8CA9
#define GL_MAX_SAMPLES 0x8D57
GLvoid glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
GLvoid glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
// GLES 3.0 invalidate FBO
GLvoid glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments);
#endif
// EXT_texture_compression_s3tc
#ifndef GL_EXT_texture_compression_s3tc
#define GL_EXT_texture_compression_s3tc 1
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif
// IMG_texture_compression_pvrtc
#ifndef GL_IMG_texture_compression_pvrtc
#define GL_IMG_texture_compression_pvrtc 1
#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
#endif
#ifndef GL_ETC1_RGB8_OES
#define GL_ETC1_RGB8_OES (0x8D64)
#endif
// APPLE_texture_max_level, GLES 3.0
#ifndef GL_TEXTURE_MAX_LEVEL
#define GL_TEXTURE_MAX_LEVEL 0x813D
#endif
+551
View File
@@ -0,0 +1,551 @@
#include "ShaderGL.h"
#include "GeometryGL.h"
#include "DeviceGL.h"
#include "HeadersGL.h"
#include <set>
#include <map>
LOGGROUP(Graphics)
namespace RBX
{
namespace Graphics
{
static int getShaderParameter(GLuint shader, GLenum pname)
{
GLint result;
glGetShaderiv(shader, pname, &result);
return result;
}
static int getProgramParameter(GLuint program, GLenum pname)
{
GLint result;
glGetProgramiv(program, pname, &result);
return result;
}
template <typename GetParameter, typename GetLog>
static std::string getInfoLog(unsigned int id, GetParameter getParameter, GetLog getLog)
{
int infoLength = getParameter(id, GL_INFO_LOG_LENGTH);
if (infoLength > 0)
{
std::vector<char> buffer(infoLength);
getLog(id, infoLength, NULL, &buffer[0]);
return std::string(&buffer[0]);
}
else
{
return "";
}
}
template <typename GetParameter, typename GetLog>
static void dumpInfoLog(unsigned int id, GetParameter getParameter, GetLog getLog)
{
std::string log = getInfoLog(id, getParameter, getLog);
// Intel and AMD drivers like to say "No errors" for every shader.
if (strstr(log.c_str(), "warn") || strstr(log.c_str(), "Warn") || strstr(log.c_str(), "WARN"))
{
FASTLOG2(FLog::Graphics, "Shader %d has a non-empty infolog (length %d)", id, log.length());
ShaderProgram::dumpToFLog(log, FLog::Graphics);
}
}
static unsigned int compileShader(const std::string& source, GLenum type)
{
unsigned int id = glCreateShader(type);
RBXASSERT(id);
int length = source.length();
const char* data = source.c_str();
glShaderSource(id, 1, &data, &length);
glCompileShader(id);
if (getShaderParameter(id, GL_COMPILE_STATUS) != 1)
{
std::string infoLog = getInfoLog(id, getShaderParameter, glGetShaderInfoLog);
glDeleteShader(id);
throw std::runtime_error(infoLog);
}
dumpInfoLog(id, getShaderParameter, glGetShaderInfoLog);
return id;
}
static unsigned int parseAttribs(const std::string& source, const char* prefix)
{
std::set<std::string> attribs;
size_t offset = 0;
while (offset < source.length())
{
std::string::size_type start = source.find(prefix, offset);
if (start == std::string::npos)
break;
std::string::size_type eol = source.find('\n', start);
if (eol == std::string::npos)
break;
offset = eol;
std::string::size_type semi = source.find(';', start);
std::string::size_type space = source.find_last_of(' ', eol);
if ((start == 0 || source[start - 1] == '\n') && semi != std::string::npos && space != std::string::npos && space < semi)
attribs.insert(source.substr(space + 1, semi - space - 1));
}
unsigned int result = 0;
for (unsigned int attr = 0; attr < 16; ++attr)
{
const char* name = VertexLayoutGL::getShaderAttributeName(attr);
if (name && attribs.count(name))
result |= 1 << attr;
}
return result;
}
static void applyAttribs(unsigned int id, unsigned int attribMask)
{
for (unsigned int attr = 0; attr < 16; ++attr)
if (attribMask & (1 << attr))
{
const char* name = VertexLayoutGL::getShaderAttributeName(attr);
RBXASSERT(name);
glBindAttribLocation(id, attr, name);
}
}
static std::vector<FragmentShaderGL::Sampler> parseSamplers(const std::string& source)
{
std::vector<FragmentShaderGL::Sampler> result;
size_t offset = 0;
while (offset < source.length())
{
std::string::size_type start = source.find("//$$", offset);
if (start == std::string::npos)
return result;
std::string::size_type equals = source.find('=', start);
if (equals == std::string::npos)
return result;
std::string::size_type end = source.find('\n', equals);
if (end == std::string::npos)
return result;
std::string name(source.begin() + start + 4, source.begin() + equals);
std::string value(source.begin() + equals + 1, source.begin() + end);
if (!value.empty() && value[0] == 's')
{
FragmentShaderGL::Sampler entry = {name, atoi(value.c_str() + 1)};
result.push_back(entry);
}
offset = end;
}
return result;
}
static int getSamplerLocation(unsigned int id, const std::string& name)
{
int location;
if ((location = glGetUniformLocation(id, name.c_str())) >= 0)
return location;
if ((location = glGetUniformLocation(id, ("xlu_" + name).c_str())) >= 0)
return location;
return -1;
}
static unsigned int applySamplers(unsigned int id, const std::vector<FragmentShaderGL::Sampler>& samplers)
{
unsigned int mask = 0;
glUseProgram(id);
for (size_t i = 0; i < samplers.size(); ++i)
{
int location = getSamplerLocation(id, samplers[i].name);
if (location >= 0)
{
glUniform1i(location, samplers[i].slot);
mask |= 1 << samplers[i].slot;
}
}
glUseProgram(0);
return mask;
}
static ShaderProgramGL::Uniform::Type getUniformType(GLenum type)
{
switch (type)
{
case GL_FLOAT:
return ShaderProgramGL::Uniform::Type_Float;
case GL_FLOAT_VEC2:
return ShaderProgramGL::Uniform::Type_Float2;
case GL_FLOAT_VEC3:
return ShaderProgramGL::Uniform::Type_Float3;
case GL_FLOAT_VEC4:
return ShaderProgramGL::Uniform::Type_Float4;
case GL_FLOAT_MAT4:
return ShaderProgramGL::Uniform::Type_Float4x4;
default:
return ShaderProgramGL::Uniform::Type_Unknown;
}
}
typedef std::map<std::string, ShaderProgramGL::Uniform> UniformTable;
static UniformTable extractUniforms(unsigned int id)
{
int uniformCount = getProgramParameter(id, GL_ACTIVE_UNIFORMS);
int uniformMaxLength = getProgramParameter(id, GL_ACTIVE_UNIFORM_MAX_LENGTH);
std::vector<char> uniformName(uniformMaxLength + 1);
UniformTable result;
for (int i = 0; i < uniformCount; ++i)
{
GLint size;
GLenum type;
glGetActiveUniform(id, i, uniformMaxLength, NULL, &size, &type, &uniformName[0]);
ShaderProgramGL::Uniform::Type uniformType = getUniformType(type);
int location = glGetUniformLocation(id, &uniformName[0]);
if (location >= 0 && uniformType != ShaderProgramGL::Uniform::Type_Unknown)
{
ShaderProgramGL::Uniform uniform = {location, uniformType, static_cast<unsigned int>(size), static_cast<unsigned int>(0)};
std::string name(&uniformName[0]);
// Remove xlu_ prefix for entrypoint-local uniforms
if (name.compare(0, 4, "xlu_") == 0)
name.erase(name.begin(), name.begin() + 4);
// Remove [0] from arrays
std::string::size_type index = name.find('[');
if (index != std::string::npos)
name.erase(name.begin() + index, name.end());
result[name] = uniform;
}
}
return result;
}
static const ShaderProgramGL::Uniform* findUniform(const UniformTable& ctab, const std::string& name)
{
UniformTable::const_iterator it = ctab.find(name);
return (it == ctab.end()) ? NULL : &it->second;
}
static inline void transposeMatrix(float* dest, const float* source, const float* lastColumn)
{
dest[0] = source[0]; dest[1] = source[4]; dest[2] = source[8]; dest[3] = lastColumn[0];
dest[4] = source[1]; dest[5] = source[5]; dest[6] = source[9]; dest[7] = lastColumn[1];
dest[8] = source[2]; dest[9] = source[6]; dest[10] = source[10]; dest[11] = lastColumn[2];
dest[12] = source[3]; dest[13] = source[7]; dest[14] = source[11]; dest[15] = lastColumn[3];
}
VertexShaderGL::VertexShaderGL(Device* device, const std::string& source)
: VertexShader(device)
, id(0)
, attribMask(0)
{
id = compileShader(source, GL_VERTEX_SHADER);
attribMask = parseAttribs(source, static_cast<DeviceGL*>(device)->getCapsGL().ext3 ? "in " : "attribute ");
}
VertexShaderGL::~VertexShaderGL()
{
glDeleteShader(id);
}
void VertexShaderGL::reloadBytecode(const std::vector<char>& bytecode)
{
throw std::runtime_error("Bytecode reloading is not supported");
}
FragmentShaderGL::FragmentShaderGL(Device* device, const std::string& source)
: FragmentShader(device)
, id(0)
{
id = compileShader(source, GL_FRAGMENT_SHADER);
samplers = parseSamplers(source);
}
FragmentShaderGL::~FragmentShaderGL()
{
glDeleteShader(id);
}
void FragmentShaderGL::reloadBytecode(const std::vector<char>& bytecode)
{
throw std::runtime_error("Bytecode reloading is not supported");
}
ShaderProgramGL::ShaderProgramGL(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
: ShaderProgram(device, vertexShader, fragmentShader)
, id(0)
, uniformWorldMatrix(-1)
, uniformWorldMatrixArray(-1)
, cachedGlobalVersion(0)
, maxWorldTransforms(0)
, samplerMask(0)
{
id = glCreateProgram();
RBXASSERT(id);
glAttachShader(id, static_cast<VertexShaderGL*>(vertexShader.get())->getId());
glAttachShader(id, static_cast<FragmentShaderGL*>(fragmentShader.get())->getId());
// Setup attribute locations
applyAttribs(id, static_cast<VertexShaderGL*>(vertexShader.get())->getAttribMask());
glLinkProgram(id);
if (getProgramParameter(id, GL_LINK_STATUS) != 1)
{
std::string infoLog = getInfoLog(id, getProgramParameter, glGetProgramInfoLog);
glDeleteProgram(id);
throw std::runtime_error(infoLog);
}
dumpInfoLog(id, getProgramParameter, glGetProgramInfoLog);
// Make sure the program uses correct sampler slot assignments
// Note: this resets current program to 0!
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedProgram();
samplerMask = applySamplers(id, static_cast<FragmentShaderGL*>(fragmentShader.get())->getSamplers());
// Populate constant tables
const std::vector<ShaderGlobalConstant>& globalConstants = static_cast<DeviceGL*>(device)->getGlobalConstants();
UniformTable ctab = extractUniforms(id);
for (size_t i = 0; i < globalConstants.size(); ++i)
if (const Uniform* uniform = findUniform(ctab, globalConstants[i].name))
{
Uniform u = *uniform;
u.offset = globalConstants[i].offset;
globalUniforms.push_back(u);
ctab.erase(globalConstants[i].name);
}
// Populate world matrix information
if (const Uniform* uniform = findUniform(ctab, "WorldMatrix"))
{
RBXASSERT(uniform->type == Uniform::Type_Float4x4);
uniformWorldMatrix = uniform->location;
maxWorldTransforms = 1;
ctab.erase("WorldMatrix");
}
if (const Uniform* uniform = findUniform(ctab, "WorldMatrixArray"))
{
RBXASSERT(uniform->type == Uniform::Type_Float4);
RBXASSERT(uniform->size % 3 == 0);
uniformWorldMatrixArray = uniform->location;
maxWorldTransforms = uniform->size / 3;
ctab.erase("WorldMatrixArray");
}
// Populate uniform information
for (UniformTable::iterator it = ctab.begin(); it != ctab.end(); ++it)
{
uniforms.push_back(std::make_pair(it->first, it->second));
}
}
ShaderProgramGL::~ShaderProgramGL()
{
glDeleteProgram(id);
// Shader program destruction invalidates currently bound program id
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedProgram();
}
int ShaderProgramGL::getConstantHandle(const char* name) const
{
for (size_t i = 0; i < uniforms.size(); ++i)
if (uniforms[i].first == name)
return i;
return -1;
}
unsigned int ShaderProgramGL::getMaxWorldTransforms() const
{
return maxWorldTransforms;
}
unsigned int ShaderProgramGL::getSamplerMask() const
{
return samplerMask;
}
void ShaderProgramGL::bind(const void* globalData, unsigned int globalVersion, ShaderProgramGL** cache)
{
if (*cache != this)
{
*cache = this;
glUseProgram(id);
}
if (cachedGlobalVersion != globalVersion)
{
cachedGlobalVersion = globalVersion;
for (size_t i = 0; i < globalUniforms.size(); ++i)
{
const Uniform& u = globalUniforms[i];
const float* data = reinterpret_cast<const float*>(static_cast<const char*>(globalData) + u.offset);
switch (u.type)
{
case Uniform::Type_Float:
glUniform1fv(u.location, 1, data);
break;
case Uniform::Type_Float2:
glUniform2fv(u.location, 1, data);
break;
case Uniform::Type_Float3:
glUniform3fv(u.location, 1, data);
break;
case Uniform::Type_Float4:
glUniform4fv(u.location, 1, data);
break;
case Uniform::Type_Float4x4:
{
float matrix[16];
transposeMatrix(matrix, data, data + 12);
glUniformMatrix4fv(u.location, 1, false, matrix);
break;
}
default:
break;
}
}
}
}
void ShaderProgramGL::setWorldTransforms4x3(const float* data, size_t matrixCount)
{
if (matrixCount == 0)
return;
if (uniformWorldMatrix >= 0)
{
static const float lastColumn[] = {0, 0, 0, 1};
float matrix[16];
transposeMatrix(matrix, data, lastColumn);
glUniformMatrix4fv(uniformWorldMatrix, 1, false, matrix);
}
if (uniformWorldMatrixArray >= 0)
{
RBXASSERT(matrixCount <= maxWorldTransforms);
glUniform4fv(uniformWorldMatrixArray, matrixCount * 3, data);
}
}
void ShaderProgramGL::setConstant(int handle, const float* data, size_t vectorCount)
{
if (handle < 0)
return;
RBXASSERT(static_cast<size_t>(handle) < uniforms.size());
const Uniform& u = uniforms[handle].second;
switch (u.type)
{
case Uniform::Type_Float:
RBXASSERT(vectorCount == 1);
glUniform1fv(u.location, 1, data);
break;
case Uniform::Type_Float2:
RBXASSERT(vectorCount == 1);
glUniform2fv(u.location, 1, data);
break;
case Uniform::Type_Float3:
RBXASSERT(vectorCount == 1);
glUniform3fv(u.location, 1, data);
break;
case Uniform::Type_Float4:
RBXASSERT(vectorCount > 0 && vectorCount <= u.size);
glUniform4fv(u.location, vectorCount, data);
break;
default:
RBXASSERT(false);
}
}
}
}
+108
View File
@@ -0,0 +1,108 @@
#pragma once
#include "GfxCore/Shader.h"
#include <vector>
namespace RBX
{
namespace Graphics
{
class VertexShaderGL: public VertexShader
{
public:
VertexShaderGL(Device* device, const std::string& source);
~VertexShaderGL();
virtual void reloadBytecode(const std::vector<char>& bytecode);
unsigned int getId() const { return id; }
unsigned int getAttribMask() const { return attribMask; }
private:
unsigned int id;
unsigned int attribMask;
};
class FragmentShaderGL: public FragmentShader
{
public:
struct Sampler
{
std::string name;
int slot;
};
FragmentShaderGL(Device* device, const std::string& source);
~FragmentShaderGL();
virtual void reloadBytecode(const std::vector<char>& bytecode);
unsigned int getId() const { return id; }
const std::vector<Sampler>& getSamplers() const { return samplers; }
private:
unsigned int id;
std::vector<Sampler> samplers;
};
class ShaderProgramGL: public ShaderProgram
{
public:
struct Uniform
{
enum Type
{
Type_Unknown,
Type_Float,
Type_Float2,
Type_Float3,
Type_Float4,
Type_Float4x4,
Type_Count
};
int location;
Type type;
unsigned int size;
unsigned int offset;
};
ShaderProgramGL(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
~ShaderProgramGL();
virtual int getConstantHandle(const char* name) const;
virtual unsigned int getMaxWorldTransforms() const;
virtual unsigned int getSamplerMask() const;
void bind(const void* globalData, unsigned int globalVersion, ShaderProgramGL** cache);
void setWorldTransforms4x3(const float* data, size_t matrixCount);
void setConstant(int handle, const float* data, size_t vectorCount);
unsigned int getId() const { return id; }
private:
unsigned int id;
int uniformWorldMatrix;
int uniformWorldMatrixArray;
unsigned int cachedGlobalVersion;
std::vector<Uniform> globalUniforms;
std::vector<std::pair<std::string, Uniform> > uniforms;
unsigned int maxWorldTransforms;
unsigned int samplerMask;
};
}
}
+549
View File
@@ -0,0 +1,549 @@
#include "TextureGL.h"
#include "DeviceGL.h"
#include "FramebufferGL.h"
#include "HeadersGL.h"
LOGGROUP(Graphics)
FASTFLAG(GraphicsTextureCommitChanges)
namespace RBX
{
namespace Graphics
{
static const GLenum gTextureTargetGL[Texture::Type_Count] =
{
GL_TEXTURE_2D,
GL_TEXTURE_3D,
GL_TEXTURE_CUBE_MAP
};
struct TextureFormatGL
{
GLenum internalFormat;
GLenum dataFormat;
GLenum dataType;
};
static const TextureFormatGL gTextureFormatGL[Texture::Format_Count] =
{
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE },
{ GL_RG8, GL_RG, GL_UNSIGNED_BYTE },
{ GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 },
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE },
{ GL_RG16, GL_RG, GL_UNSIGNED_SHORT },
{ GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT },
{ GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, 0 },
{ GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, 0 },
{ GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, 0 },
{ GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, 0, 0 },
{ GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, 0, 0 },
{ GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 0, 0 },
{ GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 0, 0 },
{ GL_ETC1_RGB8_OES, 0, 0 },
{ GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT },
{ GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8 },
};
const TextureFormatGL& getTextureFormatGL(Texture::Format format, bool ext3)
{
if (!ext3 && format == Texture::Format_L8)
{
static const TextureFormatGL result = { GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE };
return result;
}
if (!ext3 && format == Texture::Format_LA8)
{
static const TextureFormatGL result = { GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE };
return result;
}
return gTextureFormatGL[format];
}
static const GLenum gSamplerFilterMinGL[SamplerState::Filter_Count][2] =
{
{ GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST },
{ GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR },
{ GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR }
};
static const GLenum gSamplerFilterMagGL[SamplerState::Filter_Count] =
{
GL_NEAREST,
GL_LINEAR,
GL_LINEAR
};
static const GLenum gSamplerAddressGL[SamplerState::Address_Count] =
{
GL_REPEAT,
GL_CLAMP_TO_EDGE
};
static bool doesFormatSupportPartialUpload(Texture::Format format)
{
return format != Texture::Format_ETC1;
}
static unsigned int createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, const DeviceCapsGL& caps)
{
GLenum target = gTextureTargetGL[type];
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
unsigned int id = 0;
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &id);
glBindTexture(target, id);
if (caps.extTextureStorage)
{
if (type == Texture::Type_3D)
{
glTexStorage3D(target, mipLevels, formatGl.internalFormat, width, height, depth);
}
else
{
glTexStorage2D(target, mipLevels, formatGl.internalFormat, width, height);
}
}
else
{
// Is this correct? There's a subtle difference here between desktop GL and GLES
#ifdef GLES
GLenum internalFormat = formatGl.dataFormat;
#else
GLenum internalFormat = formatGl.internalFormat;
#endif
unsigned int faces = (type == Texture::Type_Cube) ? 6 : 1;
for (unsigned int index = 0; index < faces; ++index)
{
GLenum faceTarget = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
for (unsigned int mip = 0; mip < mipLevels; ++mip)
{
unsigned int mipWidth = Texture::getMipSide(width, mip);
unsigned int mipHeight = Texture::getMipSide(height, mip);
unsigned int mipDepth = Texture::getMipSide(depth, mip);
if (type == Texture::Type_3D)
{
glTexImage3D(faceTarget, mip, internalFormat, mipWidth, mipHeight, mipDepth, 0, formatGl.dataFormat, formatGl.dataType, NULL);
}
else if (Texture::isFormatCompressed(format))
{
unsigned int size = Texture::getImageSize(format, mipWidth, mipHeight);
// glCompressedTexImage does not accept NULL as a valid pointer
std::vector<char> data(size);
glCompressedTexImage2D(faceTarget, mip, formatGl.internalFormat, mipWidth, mipHeight, 0, size, &data[0]);
}
else
{
glTexImage2D(faceTarget, mip, internalFormat, mipWidth, mipHeight, 0, formatGl.dataFormat, formatGl.dataType, NULL);
}
}
}
}
if (caps.supportsTexturePartialMipChain)
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipLevels - 1);
else
RBXASSERT(mipLevels == 1 || mipLevels == Texture::getMaxMipCount(width, height, depth));
glBindTexture(target, 0);
return id;
}
static void uploadTexture(unsigned int id, Texture::Type type, Texture::Format format, unsigned int index, unsigned int mip, const TextureRegion& region, bool entireRegion, const void* data, const DeviceCapsGL& caps)
{
GLenum target = gTextureTargetGL[type];
GLenum faceTarget = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
glActiveTexture(GL_TEXTURE0);
glBindTexture(target, id);
bool needsCustomAlignment = (!Texture::isFormatCompressed(format) && Texture::getFormatBits(format) < 32);
glPixelStorei(GL_UNPACK_ALIGNMENT, needsCustomAlignment ? 1 : 4);
if (type == Texture::Type_3D)
{
glTexSubImage3D(faceTarget, mip, region.x, region.y, region.z, region.width, region.height, region.depth, formatGl.dataFormat, formatGl.dataType, data);
}
else
{
if (Texture::isFormatCompressed(format))
{
unsigned int size = Texture::getImageSize(format, region.width, region.height) * region.depth;
if (entireRegion && !doesFormatSupportPartialUpload(format) && !caps.extTextureStorage)
{
glCompressedTexImage2D(faceTarget, mip, formatGl.internalFormat, region.width, region.height, 0, size, data);
}
else
{
glCompressedTexSubImage2D(faceTarget, mip, region.x, region.y, region.width, region.height, formatGl.internalFormat, size, data);
}
}
else
{
glTexSubImage2D(faceTarget, mip, region.x, region.y, region.width, region.height, formatGl.dataFormat, formatGl.dataType, data);
}
}
glBindTexture(target, 0);
}
TextureGL::TextureGL(Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage)
: Texture(device, type, format, width, height, depth, mipLevels, usage)
, id(0)
, cachedState(SamplerState::Filter_Count) // initialize state to invalid value to force a cache miss on the next setup
, external(false)
, scratchId(0)
, scratchOffset(0)
, scratchSize(0)
{
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
if (!formatGl.internalFormat)
throw RBX::runtime_error("Unsupported format: %d (platform)", format);
if ((format == Format_RGBA16F) && !caps.supportsTextureHalfFloat)
throw RBX::runtime_error("Unsupported format: %d (caps)", format);
if ((format == Format_BC1 || format == Format_BC2 || format == Format_BC3) && !caps.supportsTextureDXT)
throw RBX::runtime_error("Unsupported format: %d (caps)", format);
if ((format == Format_PVRTC_RGB2 || format == Format_PVRTC_RGBA2 || format == Format_PVRTC_RGB4 || format == Format_PVRTC_RGBA4) && !caps.supportsTexturePVR)
throw RBX::runtime_error("Unsupported format: %d (caps)", format);
if (format == Format_ETC1 && !caps.supportsTextureETC1)
throw RBX::runtime_error("Unsupported format: %d (capse)", format);
if (!caps.supportsTextureNPOT && ((width & (width - 1)) || (height & (height - 1)) || (depth & (depth - 1))) && (usage != Usage_Renderbuffer || mipLevels > 1))
throw RBX::runtime_error("Unsupported dimensions: %dx%dx%d (NPOT)", width, height, depth);
if (!caps.supportsTexture3D && type == Type_3D)
throw RBX::runtime_error("Unsupported type: 3D");
if (FFlag::GraphicsTextureCommitChanges && usage == Usage_Dynamic && isFormatCompressed(format))
throw RBX::runtime_error("Unsupported format: compressed formats are not supported for dynamic textures");
// createTexture poisons the binding on the first stage
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
id = createTexture(type, format, width, height, depth, mipLevels, caps);
if (FFlag::GraphicsTextureCommitChanges && usage == Usage_Dynamic && caps.ext3)
{
scratchSize = getTextureSize(type, format, width, height, depth, mipLevels);
glGenBuffers(1, &scratchId);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
glBufferData(GL_PIXEL_UNPACK_BUFFER, scratchSize, NULL, GL_DYNAMIC_COPY);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
}
TextureGL::TextureGL(Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage, unsigned int id)
: Texture(device, type, format, width, height, depth, mipLevels, usage)
, cachedState(SamplerState::Filter_Count) // initialize state to invalid value to force a cache miss on the next setup
, id(id)
, external(true)
, scratchId(0)
, scratchOffset(0)
, scratchSize(0)
{
}
TextureGL::~TextureGL()
{
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTexture(this);
if (scratchId)
glDeleteBuffers(1, &scratchId);
if (!external)
glDeleteTextures(1, &id);
}
void TextureGL::upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size)
{
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
RBXASSERT(index < (type == Type_Cube ? 6u : 1u));
unsigned int mipWidth = getMipSide(width, mip);
unsigned int mipHeight = getMipSide(height, mip);
unsigned int mipDepth = getMipSide(depth, mip);
RBXASSERT(mip < mipLevels);
RBXASSERT(region.x + region.width <= mipWidth);
RBXASSERT(region.y + region.height <= mipHeight);
RBXASSERT(region.z + region.depth <= mipDepth);
RBXASSERT(size == getImageSize(format, region.width, region.height) * region.depth);
if (supportsLocking())
{
unsigned int bpp = getFormatBits(format) / 8;
LockResult lr = lock(index, mip, region);
for (unsigned int z = 0; z < region.depth; ++z)
for (unsigned int y = 0; y < region.height; ++y)
{
unsigned int sourceOffset = (region.width * region.height * z + region.width * y) * bpp;
unsigned int targetOffset = lr.slicePitch * z + lr.rowPitch * y;
memcpy(static_cast<char*>(lr.data) + targetOffset, static_cast<const char*>(data) + sourceOffset, region.width * bpp);
}
unlock(index, mip);
}
else
{
// uploadTexture poisons the binding on the first stage
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
bool entireRegion = (region.x == 0 && region.y == 0 && region.width == mipWidth && region.height == mipHeight);
uploadTexture(id, type, format, index, mip, region, entireRegion, data, caps);
}
}
#ifdef GLES
bool TextureGL::download(unsigned int index, unsigned int mip, void* data, unsigned int size)
{
return false;
}
#else
bool TextureGL::download(unsigned int index, unsigned int mip, void* data, unsigned int size)
{
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
GLenum target = gTextureTargetGL[type];
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
RBXASSERT(index < (type == Type_Cube ? 6u : 1u));
GLenum faceTarget = (type == Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
unsigned int mipWidth = getMipSide(width, mip);
unsigned int mipHeight = getMipSide(height, mip);
unsigned int mipDepth = getMipSide(depth, mip);
RBXASSERT(mip < mipLevels);
RBXASSERT(size == getImageSize(format, mipWidth, mipHeight) * mipDepth);
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(target, id);
bool needsCustomAlignment = (!isFormatCompressed(format) && getFormatBits(format) < 32);
glPixelStorei(GL_PACK_ALIGNMENT, needsCustomAlignment ? 1 : 4);
if (isFormatCompressed(format))
glGetCompressedTexImage(faceTarget, mip, data);
else
glGetTexImage(faceTarget, mip, formatGl.dataFormat, formatGl.dataType, data);
glBindTexture(target, 0);
return true;
}
#endif
bool TextureGL::supportsLocking() const
{
if (FFlag::GraphicsTextureCommitChanges)
return scratchId != 0;
return false;
}
Texture::LockResult TextureGL::lock(unsigned int index, unsigned int mip, const TextureRegion& region)
{
if (!scratchId)
{
Texture::LockResult result = {0, 0, 0};
return result;
}
RBXASSERT(index < (type == Type_Cube ? 6u : 1u));
unsigned int mipWidth = getMipSide(width, mip);
unsigned int mipHeight = getMipSide(height, mip);
unsigned int mipDepth = getMipSide(depth, mip);
RBXASSERT(mip < mipLevels);
RBXASSERT(region.x + region.width <= mipWidth);
RBXASSERT(region.y + region.height <= mipHeight);
RBXASSERT(region.z + region.depth <= mipDepth);
unsigned int size = getImageSize(format, region.width, region.height) * region.depth;
unsigned int flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
if (scratchOffset + size > scratchSize)
{
commitChanges();
scratchOffset = 0;
flags = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT;
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
void* data = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, scratchOffset, size, flags);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
Change change = { index, mip, region, scratchOffset };
scratchOffset += size;
pendingChanges.push_back(change);
Texture::LockResult result = {data, getImageSize(format, region.width, 1), getImageSize(format, region.width, region.height) };
return result;
}
void TextureGL::unlock(unsigned int index, unsigned int mip)
{
if (!scratchId)
return;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
shared_ptr<Renderbuffer> TextureGL::getRenderbuffer(unsigned int index, unsigned int mip)
{
RBXASSERT(usage == Usage_Renderbuffer);
RBXASSERT(mip == 0);
RBXASSERT(index < (type == Type_Cube ? 6u : 1u));
weak_ptr<Renderbuffer>& slot = renderBuffers[std::make_pair(index,mip)];
shared_ptr<Renderbuffer> rb = slot.lock();
if (!rb)
{
GLenum target = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : gTextureTargetGL[type];
rb.reset(new RenderbufferGL(device, shared_from_this(), target));
slot = rb;
}
return rb;
}
void TextureGL::commitChanges()
{
if (!FFlag::GraphicsTextureCommitChanges)
return;
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
if (pendingChanges.empty())
return;
// uploadTexture poisons the binding on the first stage
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
for (auto& change: pendingChanges)
{
RBXASSERT(change.scratchOffset < scratchOffset);
bool entireRegion = false; // this is only important for compressed formats that we don't support
uploadTexture(id, type, format, change.index, change.mip, change.region, entireRegion, reinterpret_cast<void*>(change.scratchOffset), caps);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pendingChanges.clear();
}
void TextureGL::generateMipmaps()
{
RBXASSERT(usage != Usage_Dynamic);
if (mipLevels <= 1)
return;
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(gTextureTargetGL[type], id);
glGenerateMipmap(gTextureTargetGL[type]);
glBindTexture(gTextureTargetGL[type], 0);
}
void TextureGL::bind(unsigned int stage, const SamplerState& state, TextureGL** cache)
{
if (*cache != this || cachedState != state)
{
glActiveTexture(GL_TEXTURE0 + stage);
GLenum target = gTextureTargetGL[type];
if (*cache != this)
{
*cache = this;
glBindTexture(target, id);
}
if (cachedState != state)
{
cachedState = state;
bool hasMips = mipLevels > 1;
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, gSamplerFilterMinGL[state.getFilter()][hasMips]);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, gSamplerFilterMagGL[state.getFilter()]);
glTexParameteri(target, GL_TEXTURE_WRAP_S, gSamplerAddressGL[state.getAddress()]);
glTexParameteri(target, GL_TEXTURE_WRAP_T, gSamplerAddressGL[state.getAddress()]);
#ifndef GLES
int anisotropy = state.getFilter() == SamplerState::Filter_Anisotropic ? state.getAnisotropy() : 1;
glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast<float>(anisotropy));
#endif
}
}
}
unsigned int TextureGL::getTarget() const
{
return gTextureTargetGL[type];
}
unsigned int TextureGL::getInternalFormat(Format format)
{
return gTextureFormatGL[format].internalFormat;
}
}
}
+68
View File
@@ -0,0 +1,68 @@
#pragma once
#include "GfxCore/Texture.h"
#include "GfxCore/States.h"
#include <boost/enable_shared_from_this.hpp>
#include <map>
#include <vector>
namespace RBX
{
namespace Graphics
{
class TextureGL: public Texture, public boost::enable_shared_from_this<TextureGL>
{
public:
TextureGL(Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage);
TextureGL(Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage, unsigned int id);
~TextureGL();
virtual void upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size);
virtual bool download(unsigned int index, unsigned int mip, void* data, unsigned int size);
virtual bool supportsLocking() const;
virtual LockResult lock(unsigned int index, unsigned int mip, const TextureRegion& region);
virtual void unlock(unsigned int index, unsigned int mip);
virtual shared_ptr<Renderbuffer> getRenderbuffer(unsigned int index, unsigned int mip);
virtual void commitChanges();
virtual void generateMipmaps();
void bind(unsigned int stage, const SamplerState& state, TextureGL** cache);
unsigned int getId() const { return id; }
unsigned int getTarget() const;
static unsigned int getInternalFormat(Format format);
private:
struct Change
{
unsigned int index;
unsigned int mip;
TextureRegion region;
unsigned int scratchOffset;
};
unsigned int id;
SamplerState cachedState;
bool external;
unsigned int scratchId;
unsigned int scratchOffset;
unsigned int scratchSize;
std::vector<Change> pendingChanges;
typedef std::map<std::pair<unsigned,unsigned>, weak_ptr<Renderbuffer> > RenderBufferMap;
RenderBufferMap renderBuffers;
};
}
}