From f3e5306f901cc392956e1465dfea15d63a693866 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 5 Apr 2016 13:25:00 -0700 Subject: [PATCH 1/7] Working on an automated performance test --- examples/tests/playaPerformanceTest.js | 11 + examples/tests/playaPerformanceTest.qml | 193 ++++++++++++++++++ interface/src/Application.cpp | 13 +- .../src/FrameTimingsScriptingInterface.cpp | 53 +++++ .../src/FrameTimingsScriptingInterface.h | 38 ++++ interface/src/avatar/MyAvatar.cpp | 9 + interface/src/avatar/MyAvatar.h | 4 + libraries/gpu/src/gpu/GLBackendTexture.cpp | 7 +- 8 files changed, 324 insertions(+), 4 deletions(-) create mode 100644 examples/tests/playaPerformanceTest.js create mode 100644 examples/tests/playaPerformanceTest.qml create mode 100644 interface/src/FrameTimingsScriptingInterface.cpp create mode 100644 interface/src/FrameTimingsScriptingInterface.h diff --git a/examples/tests/playaPerformanceTest.js b/examples/tests/playaPerformanceTest.js new file mode 100644 index 0000000000..4c8a728a15 --- /dev/null +++ b/examples/tests/playaPerformanceTest.js @@ -0,0 +1,11 @@ +var qml = Script.resolvePath('playaPerformanceTest.qml'); +qmlWindow = new OverlayWindow({ + title: 'Test Qml', + source: qml, + height: 320, + width: 640, + toolWindow: false, + visible: true +}); + + diff --git a/examples/tests/playaPerformanceTest.qml b/examples/tests/playaPerformanceTest.qml new file mode 100644 index 0000000000..f1382358ae --- /dev/null +++ b/examples/tests/playaPerformanceTest.qml @@ -0,0 +1,193 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +Rectangle { + id: root + width: parent ? parent.width : 100 + height: parent ? parent.height : 100 + + signal sendToScript(var message); + property var values: []; + property var host: AddressManager.hostname + + + Component.onCompleted: { + Window.domainChanged.connect(function(newDomain){ + if (newDomain !== root.host) { + root.host = AddressManager.hostname; + } + }); + } + + onHostChanged: { + if (root.running) { + if (host !== "Dreaming" && host !== "Playa") { + return; + } + + console.log("PERF new domain " + host) + if (host === "Dreaming") { + AddressManager.handleLookupString("Playa"); + return; + } + + if (host === "Playa") { + console.log("PERF starting timers and frame timing"); + // If we've arrived, start running the test + FrameTimings.start(); + rotationTimer.start(); + stopTimer.start(); + } + } + } + + function startTest() { + console.log("PERF startTest()"); + root.running = true + console.log("PERF current host: " + AddressManager.hostname) + // If we're already in playa, we need to go somewhere else... + if ("Playa" === AddressManager.hostname) { + console.log("PERF Navigating to dreaming") + AddressManager.handleLookupString("Dreaming/0,0,0"); + } else { + console.log("PERF Navigating to playa") + AddressManager.handleLookupString("Playa"); + } + } + + function stopTest() { + console.log("PERF stopTest()"); + root.running = false; + stopTimer.stop(); + rotationTimer.stop(); + FrameTimings.finish(); + root.values = FrameTimings.getValues(); + AddressManager.handleLookupString("Dreaming/0,0,0"); + resultGraph.requestPaint(); + console.log("PERF Value Count: " + root.values.length); + console.log("PERF Max: " + FrameTimings.max); + console.log("PERF Min: " + FrameTimings.min); + console.log("PERF Avg: " + FrameTimings.mean); + console.log("PERF StdDev: " + FrameTimings.standardDeviation); + } + + function yaw(a) { + var y = -Math.sin( a / 2.0 ); + var w = Math.cos( a / 2.0 ); + var l = Math.sqrt((y * y) + (w * w)); + return Qt.quaternion(w / l, 0, y / l, 0); + } + + function rotate() { + MyAvatar.setOrientationVar(yaw(Date.now() / 1000)); + } + + property bool running: false + + Timer { + id: stopTimer + interval: 30 * 1000 + repeat: false + running: false + onTriggered: stopTest(); + } + + Timer { + id: rotationTimer + interval: 100 + repeat: true + running: false + onTriggered: rotate(); + } + + Row { + id: row + anchors { left: parent.left; right: parent.right; } + spacing: 8 + Button { + text: root.running ? "Stop" : "Run" + onClicked: root.running ? stopTest() : startTest(); + } + } + +// Rectangle { +// anchors { left: parent.left; right: parent.right; top: row.bottom; topMargin: 8; bottom: parent.bottom; } +// //anchors.fill: parent +// color: "#7fff0000" +// } + + // Return the maximum value from a set of values + function vv(i, max) { + var perValue = values.length / max; + var start = Math.floor(perValue * i); + var end = Math.min(values.length, Math.floor(start + perValue)); + var result = 0; + for (var j = start; j <= end; ++j) { + result = Math.max(result, values[j]); + } + return result; + } + + Canvas { + id: resultGraph + anchors { left: parent.left; right: parent.right; top: row.bottom; margins: 16; bottom: parent.bottom; } + property real maxValue: 200; + property real perFrame: 10000; + property real k1: (5 / maxValue) * height; + property real k2: (10 / maxValue) * height; + property real k3: (100 / maxValue) * height; + + onPaint: { + var ctx = getContext("2d"); + if (values.length === 0) { + ctx.fillStyle = Qt.rgba(1, 0, 0, 1); + ctx.fillRect(0, 0, width, height); + return; + } + + + //ctx.setTransform(1, 0, 0, -1, 0, 0); + ctx.fillStyle = Qt.rgba(0, 0, 0, 1); + ctx.fillRect(0, 0, width, height); + + ctx.strokeStyle= "gray"; + ctx.lineWidth="1"; + ctx.beginPath(); + for (var i = 0; i < width; ++i) { + var value = vv(i, width); //values[Math.min(i, values.length - 1)]; + value /= 10000; + value /= maxValue; + ctx.moveTo(i, height); + ctx.lineTo(i, height - (height * value)); + } + ctx.stroke(); + + ctx.strokeStyle= "green"; + ctx.lineWidth="2"; + ctx.beginPath(); + var lineHeight = height - k1; + ctx.moveTo(0, lineHeight); + ctx.lineTo(width, lineHeight); + ctx.stroke(); + + ctx.strokeStyle= "yellow"; + ctx.lineWidth="2"; + ctx.beginPath(); + lineHeight = height - k2; + ctx.moveTo(0, lineHeight); + ctx.lineTo(width, lineHeight); + ctx.stroke(); + + ctx.strokeStyle= "red"; + ctx.lineWidth="2"; + ctx.beginPath(); + lineHeight = height - k3; + ctx.moveTo(0, lineHeight); + ctx.lineTo(width, lineHeight); + ctx.stroke(); + + } + } +} + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3a0b0998c5..f8bfd8f3d4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -147,6 +147,8 @@ #include "Util.h" #include "InterfaceParentFinder.h" +#include "FrameTimingsScriptingInterface.h" + // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. #if defined(Q_OS_WIN) @@ -1334,6 +1336,8 @@ void Application::initializeGL() { InfoView::show(INFO_HELP_PATH, true); } +FrameTimingsScriptingInterface _frameTimingsScriptingInterface; + extern void setupPreferences(); void Application::initializeUi() { @@ -1378,6 +1382,8 @@ void Application::initializeUi() { rootContext->setContextProperty("Messages", DependencyManager::get().data()); rootContext->setContextProperty("Recording", DependencyManager::get().data()); rootContext->setContextProperty("Preferences", DependencyManager::get().data()); + rootContext->setContextProperty("AddressManager", DependencyManager::get().data()); + rootContext->setContextProperty("FrameTimings", &_frameTimingsScriptingInterface); rootContext->setContextProperty("TREE_SCALE", TREE_SCALE); rootContext->setContextProperty("Quat", new Quat()); @@ -1421,6 +1427,7 @@ void Application::initializeUi() { rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface()); rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); + _glWidget->installEventFilter(offscreenUi.data()); offscreenUi->setMouseTranslator([=](const QPointF& pt) { @@ -1463,9 +1470,9 @@ void Application::initializeUi() { }); } + void Application::paintGL() { updateHeartbeat(); - // Some plugins process message events, potentially leading to // re-entering a paint event. don't allow further processing if this // happens @@ -1483,6 +1490,7 @@ void Application::paintGL() { _frameCount++; _frameCounter.increment(); + auto lastPaintBegin = usecTimestampNow(); PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount); PerformanceTimer perfTimer("paintGL"); @@ -1735,6 +1743,9 @@ void Application::paintGL() { batch.resetStages(); }); } + + uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; + _frameTimingsScriptingInterface.addValue(lastPaintDuration); } void Application::runTests() { diff --git a/interface/src/FrameTimingsScriptingInterface.cpp b/interface/src/FrameTimingsScriptingInterface.cpp new file mode 100644 index 0000000000..6c8c164804 --- /dev/null +++ b/interface/src/FrameTimingsScriptingInterface.cpp @@ -0,0 +1,53 @@ +// +// Created by Bradley Austin Davis on 2016/04/04 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "FrameTimingsScriptingInterface.h" + +#include + +void FrameTimingsScriptingInterface::start() { + _values.clear(); + DependencyManager::get()->setUnusedResourceCacheSize(0); + _values.reserve(8192); + _active = true; +} + +void FrameTimingsScriptingInterface::addValue(uint64_t value) { + if (_active) { + _values.push_back(value); + } +} + +void FrameTimingsScriptingInterface::finish() { + _active = false; + uint64_t total = 0; + _min = std::numeric_limits::max(); + _max = std::numeric_limits::lowest(); + size_t count = _values.size(); + for (auto i = 0; i < count; ++i) { + const uint64_t& value = _values[i]; + _max = std::max(_max, value); + _min = std::min(_min, value); + total += value; + } + _mean = (float)total / (float)count; + float deviationTotal = 0; + for (auto i = 0; i < count; ++i) { + float deviation = _values[i] - _mean; + deviationTotal += deviation*deviation; + } + _stdDev = sqrt(deviationTotal / (float)count); +} + +QVariantList FrameTimingsScriptingInterface::getValues() const { + QVariantList result; + for (const auto& v : _values) { + result << v; + } + return result; +} diff --git a/interface/src/FrameTimingsScriptingInterface.h b/interface/src/FrameTimingsScriptingInterface.h new file mode 100644 index 0000000000..73eb1aec67 --- /dev/null +++ b/interface/src/FrameTimingsScriptingInterface.h @@ -0,0 +1,38 @@ +// +// Created by Bradley Austin Davis on 2016/04/04 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#include +#include + +class FrameTimingsScriptingInterface : public QObject { + Q_OBJECT + Q_PROPERTY(float mean READ getMean CONSTANT) + Q_PROPERTY(float max READ getMax CONSTANT) + Q_PROPERTY(float min READ getMin CONSTANT) + Q_PROPERTY(float standardDeviation READ getStandardDeviation CONSTANT) +public: + Q_INVOKABLE void start(); + Q_INVOKABLE void addValue(uint64_t value); + Q_INVOKABLE void finish(); + Q_INVOKABLE QVariantList getValues() const; + + + uint64_t getMax() const { return _max; } + uint64_t getMin() const { return _min; } + float getStandardDeviation() const { return _stdDev; } + float getMean() const { return _mean; } + +protected: + std::vector _values; + bool _active { false }; + uint64_t _max { 0 }; + uint64_t _min { 0 }; + float _stdDev { 0 }; + float _mean { 0 }; +}; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 906f7b4c9f..4472abe6c3 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -204,6 +204,15 @@ MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } +void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) { + Avatar::setOrientation(quatFromVariant(newOrientationVar)); +} + +QVariant MyAvatar::getOrientationVar() const { + return quatToVariant(Avatar::getOrientation()); +} + + // virtual void MyAvatar::simulateAttachments(float deltaTime) { // don't update attachments here, do it in harvestResultsFromPhysicsSimulation() diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 92bf9e7614..b15f812197 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -105,6 +105,10 @@ public: // thread safe Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; + Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar); + Q_INVOKABLE QVariant getOrientationVar() const; + + // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 097d7f73cd..0514411d7f 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -52,7 +52,8 @@ GLBackend::GLTexture::GLTexture(const Texture& texture) : _target(gpuToGLTextureType(texture)), _size(0), _virtualSize(0), - _numLevels(texture.maxMip() + 1), + _numLevels(1), + //_numLevels(texture.maxMip() + 1), _gpuTexture(texture) { Backend::incrementTextureGPUCount(); @@ -254,7 +255,7 @@ void GLBackend::GLTexture::transfer() const { } if (_gpuTexture.isAutogenerateMips()) { - glGenerateMipmap(_target); + //glGenerateMipmap(_target); (void)CHECK_GL_ERROR(); } } @@ -435,7 +436,7 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { glActiveTexture(GL_TEXTURE0 + bindingSlot); glBindTexture(object->_target, object->_texture); - glGenerateMipmap(object->_target); + //glGenerateMipmap(object->_target); if (freeSlot < 0) { // If had to use slot 0 then restore state From 75c99fe416e816f5155bfee9f30605df68fb0d58 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 15 Apr 2016 00:45:51 -0700 Subject: [PATCH 2/7] Working on GPU library test code --- tests/gpu-test/src/main.cpp | 474 +++++++++++++++++++++++------------- 1 file changed, 307 insertions(+), 167 deletions(-) diff --git a/tests/gpu-test/src/main.cpp b/tests/gpu-test/src/main.cpp index d1cd4389a7..1bb02b8101 100644 --- a/tests/gpu-test/src/main.cpp +++ b/tests/gpu-test/src/main.cpp @@ -38,9 +38,11 @@ #include #include +#include + #include #include -#include +#include #include "unlit_frag.h" #include "unlit_vert.h" @@ -83,6 +85,93 @@ public: uint32_t toCompactColor(const glm::vec4& color); + +const char* VERTEX_SHADER = R"SHADER( +#version 450 core + +layout(location = 0) in vec4 inPosition; +layout(location = 3) in vec2 inTexCoord0; + +struct TransformObject { + mat4 _model; + mat4 _modelInverse; +}; + +layout(location=15) in ivec2 _drawCallInfo; + +uniform samplerBuffer transformObjectBuffer; + +TransformObject getTransformObject() { + int offset = 8 * _drawCallInfo.x; + TransformObject object; + object._model[0] = texelFetch(transformObjectBuffer, offset); + object._model[1] = texelFetch(transformObjectBuffer, offset + 1); + object._model[2] = texelFetch(transformObjectBuffer, offset + 2); + object._model[3] = texelFetch(transformObjectBuffer, offset + 3); + + object._modelInverse[0] = texelFetch(transformObjectBuffer, offset + 4); + object._modelInverse[1] = texelFetch(transformObjectBuffer, offset + 5); + object._modelInverse[2] = texelFetch(transformObjectBuffer, offset + 6); + object._modelInverse[3] = texelFetch(transformObjectBuffer, offset + 7); + + return object; +} + +struct TransformCamera { + mat4 _view; + mat4 _viewInverse; + mat4 _projectionViewUntranslated; + mat4 _projection; + mat4 _projectionInverse; + vec4 _viewport; +}; + +layout(std140) uniform transformCameraBuffer { + TransformCamera _camera; +}; + +TransformCamera getTransformCamera() { + return _camera; +} + +// the interpolated normal +out vec2 _texCoord0; + +void main(void) { + _texCoord0 = inTexCoord0.st; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + { // transformModelToClipPos + vec4 eyeWAPos; + { // _transformModelToEyeWorldAlignedPos + highp mat4 _mv = obj._model; + _mv[3].xyz -= cam._viewInverse[3].xyz; + highp vec4 _eyeWApos = (_mv * inPosition); + eyeWAPos = _eyeWApos; + } + gl_Position = cam._projectionViewUntranslated * eyeWAPos; + } + +})SHADER"; + +const char* FRAGMENT_SHADER = R"SHADER( +#version 450 core + +uniform sampler2D originalTexture; + +in vec2 _texCoord0; + +layout(location = 0) out vec4 _fragColor0; + +void main(void) { + //_fragColor0 = vec4(_texCoord0, 0.0, 1.0); + _fragColor0 = texture(originalTexture, _texCoord0); +} +)SHADER"; + + gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) { auto vs = gpu::Shader::createVertex(vertexShaderSrc); auto fs = gpu::Shader::createPixel(fragmentShaderSrc); @@ -125,6 +214,7 @@ class QTestWindow : public QWindow { glm::mat4 _projectionMatrix; RateCounter fps; QTime _time; + glm::mat4 _camera; protected: void renderText(); @@ -145,7 +235,7 @@ public: setGLFormatVersion(format); format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); format.setOption(QSurfaceFormat::DebugContext); - format.setSwapInterval(0); + //format.setSwapInterval(0); setFormat(format); @@ -158,19 +248,22 @@ public: gpu::Context::init(); _context = std::make_shared(); - + makeCurrent(); auto shader = makeShader(unlit_vert, unlit_frag, gpu::Shader::BindingSet{}); auto state = std::make_shared(); state->setMultisampleEnable(true); state->setDepthTest(gpu::State::DepthTest { true }); _pipeline = gpu::Pipeline::create(shader, state); + + // Clear screen gpu::Batch batch; batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 1.0, 0.0, 0.5, 1.0 }); _context->render(batch); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); resize(QSize(800, 600)); @@ -181,182 +274,229 @@ public: virtual ~QTestWindow() { } + void updateCamera() { + float t = _time.elapsed() * 1e-4f; + glm::vec3 unitscale { 1.0f }; + glm::vec3 up { 0.0f, 1.0f, 0.0f }; + + float distance = 3.0f; + glm::vec3 camera_position { distance * sinf(t), 0.5f, distance * cosf(t) }; + + static const vec3 camera_focus(0); + static const vec3 camera_up(0, 1, 0); + _camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up)); + } + + + void drawFloorGrid(gpu::Batch& batch) { + auto geometryCache = DependencyManager::get(); + // Render grid on xz plane (not the optimal way to do things, but w/e) + // Note: GeometryCache::renderGrid will *not* work, as it is apparenly unaffected by batch rotations and renders xy only + static const std::string GRID_INSTANCE = "Grid"; + static auto compactColor1 = toCompactColor(vec4 { 0.35f, 0.25f, 0.15f, 1.0f }); + static auto compactColor2 = toCompactColor(vec4 { 0.15f, 0.25f, 0.35f, 1.0f }); + static std::vector transforms; + static gpu::BufferPointer colorBuffer; + if (!transforms.empty()) { + transforms.reserve(200); + colorBuffer = std::make_shared(); + for (int i = 0; i < 100; ++i) { + { + glm::mat4 transform = glm::translate(mat4(), vec3(0, -1, -50 + i)); + transform = glm::scale(transform, vec3(100, 1, 1)); + transforms.push_back(transform); + colorBuffer->append(compactColor1); + } + + { + glm::mat4 transform = glm::mat4_cast(quat(vec3(0, PI / 2.0f, 0))); + transform = glm::translate(transform, vec3(0, -1, -50 + i)); + transform = glm::scale(transform, vec3(100, 1, 1)); + transforms.push_back(transform); + colorBuffer->append(compactColor2); + } + } + } + auto pipeline = geometryCache->getSimplePipeline(); + for (auto& transform : transforms) { + batch.setModelTransform(transform); + batch.setupNamedCalls(GRID_INSTANCE, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { + batch.setViewTransform(_camera); + batch.setPipeline(_pipeline); + geometryCache->renderWireShapeInstances(batch, GeometryCache::Line, data.count(), colorBuffer); + }); + } + } + + void drawSimpleShapes(gpu::Batch& batch) { + auto geometryCache = DependencyManager::get(); + static const size_t ITEM_COUNT = 1000; + static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT; + static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT; + + static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; + static const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; + static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA }; + + static std::vector transforms; + static std::vector colors; + static gpu::BufferPointer colorBuffer; + static gpu::BufferView colorView; + static gpu::BufferView instanceXfmView; + if (!colorBuffer) { + colorBuffer = std::make_shared(); + + static const float ITEM_RADIUS = 20; + static const vec3 ITEM_TRANSLATION { 0, 0, -ITEM_RADIUS }; + for (size_t i = 0; i < TYPE_COUNT; ++i) { + GeometryCache::Shape shape = SHAPE[i]; + GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape]; + //indirectCommand._count + float startingInterval = ITEM_INTERVAL * i; + for (size_t j = 0; j < ITEM_COUNT; ++j) { + float theta = j * SHAPE_INTERVAL + startingInterval; + auto transform = glm::rotate(mat4(), theta, Vectors::UP); + transform = glm::rotate(transform, (randFloat() - 0.5f) * PI / 4.0f, Vectors::UNIT_X); + transform = glm::translate(transform, ITEM_TRANSLATION); + transform = glm::scale(transform, vec3(randFloat() / 2.0f + 0.5f)); + transforms.push_back(transform); + auto color = vec4 { randomColorValue(64), randomColorValue(64), randomColorValue(64), 255 }; + color /= 255.0f; + colors.push_back(color); + colorBuffer->append(toCompactColor(color)); + } + } + colorView = gpu::BufferView(colorBuffer, COLOR_ELEMENT); + } + + batch.setViewTransform(_camera); + batch.setPipeline(_pipeline); + batch.setInputFormat(getInstancedSolidStreamFormat()); + for (size_t i = 0; i < TYPE_COUNT; ++i) { + GeometryCache::Shape shape = SHAPE[i]; + GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape]; + batch.setInputBuffer(gpu::Stream::COLOR, colorView); + for (size_t j = 0; j < ITEM_COUNT; ++j) { + batch.setModelTransform(transforms[j]); + shapeData.draw(batch); + } + } + } + + void drawCenterShape(gpu::Batch& batch) { + // Render unlit cube + sphere + static auto startUsecs = usecTimestampNow(); + float seconds = getSeconds(startUsecs); + seconds /= 4.0f; + batch.setModelTransform(Transform()); + batch._glColor4f(0.8f, 0.25f, 0.25f, 1.0f); + + bool wire = (seconds - floorf(seconds) > 0.5f); + auto geometryCache = DependencyManager::get(); + int shapeIndex = ((int)seconds) % TYPE_COUNT; + if (wire) { + geometryCache->renderWireShape(batch, SHAPE[shapeIndex]); + } else { + geometryCache->renderShape(batch, SHAPE[shapeIndex]); + } + + batch.setModelTransform(Transform().setScale(2.05f)); + batch._glColor4f(1, 1, 1, 1); + geometryCache->renderWireCube(batch); + } + + void drawTerrain(gpu::Batch& batch) { + auto geometryCache = DependencyManager::get(); + static std::once_flag once; + static gpu::BufferPointer vertexBuffer { std::make_shared() }; + static gpu::BufferPointer indexBuffer { std::make_shared() }; + + static gpu::BufferView positionView; + static gpu::BufferView textureView; + static gpu::Stream::FormatPointer vertexFormat { std::make_shared() }; + + static gpu::TexturePointer texture; + static gpu::PipelinePointer pipeline; + std::call_once(once, [&] { + static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec4) * 2; // position, normals, textures + static const uint SHAPE_TEXTURES_OFFSET = sizeof(glm::vec4); + static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; + static const gpu::Element TEXTURE_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV }; + static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT16; + static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint16); + std::vector vertices; + const int MINX = -1000; + const int MAXX = 1000; + // top + vertices.push_back(vec4(MAXX, 0, MAXX, 1)); + vertices.push_back(vec4(MAXX, MAXX, 0, 0)); + + vertices.push_back(vec4(MAXX, 0, MINX, 1)); + vertices.push_back(vec4(MAXX, 0, 0, 0)); + + vertices.push_back(vec4(MINX, 0, MINX, 1)); + vertices.push_back(vec4(0, 0, 0, 0)); + + vertices.push_back(vec4(MINX, 0, MAXX, 1)); + vertices.push_back(vec4(0, MAXX, 0, 0)); + + vertexBuffer->append(vertices); + indexBuffer->append(std::vector({ 0, 1, 2, 2, 3, 0 })); + + positionView = gpu::BufferView(vertexBuffer, 0, vertexBuffer->getSize(), SHAPE_VERTEX_STRIDE, POSITION_ELEMENT); + textureView = gpu::BufferView(vertexBuffer, SHAPE_TEXTURES_OFFSET, vertexBuffer->getSize(), SHAPE_VERTEX_STRIDE, TEXTURE_ELEMENT); + texture = DependencyManager::get()->getImageTexture("C:/Users/bdavis/Git/openvr/samples/bin/cube_texture.png"); + //texture = DependencyManager::get()->getImageTexture("H:/test.png"); + //texture = DependencyManager::get()->getImageTexture("H:/crate_blue.fbm/lambert8SG_Normal_OpenGL.png"); + + auto shader = makeShader(VERTEX_SHADER, FRAGMENT_SHADER, gpu::Shader::BindingSet {}); + auto state = std::make_shared(); + state->setMultisampleEnable(false); + state->setDepthTest(gpu::State::DepthTest { true }); + pipeline = gpu::Pipeline::create(shader, state); + vertexFormat->setAttribute(gpu::Stream::POSITION); + vertexFormat->setAttribute(gpu::Stream::TEXCOORD); + + }); + batch.setPipeline(pipeline); + batch.setInputBuffer(gpu::Stream::POSITION, positionView); + batch.setInputBuffer(gpu::Stream::TEXCOORD, textureView); + batch.setIndexBuffer(gpu::UINT16, indexBuffer, 0); + batch.setInputFormat(vertexFormat); + + batch.setResourceTexture(0, texture); + batch.setModelTransform(glm::translate(glm::mat4(), vec3(0, -0.1, 0))); + batch.drawIndexed(gpu::TRIANGLES, 6, 0); + + batch.setResourceTexture(0, DependencyManager::get()->getBlueTexture()); + batch.setModelTransform(glm::translate(glm::mat4(), vec3(0, -0.2, 0))); + batch.drawIndexed(gpu::TRIANGLES, 6, 0); + } + void draw() { // Attempting to draw before we're visible and have a valid size will // produce GL errors. if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) { return; } + updateCamera(); makeCurrent(); gpu::Batch batch; - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.0f, 0.0f, 1.0f }); + batch.resetStages(); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f }); batch.clearDepthFramebuffer(1e4); batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() }); batch.setProjectionTransform(_projectionMatrix); - float t = _time.elapsed() * 1e-3f; - glm::vec3 unitscale { 1.0f }; - glm::vec3 up { 0.0f, 1.0f, 0.0f }; - - float distance = 3.0f; - glm::vec3 camera_position{ distance * sinf(t), 0.0f, distance * cosf(t) }; - - static const vec3 camera_focus(0); - static const vec3 camera_up(0, 1, 0); - glm::mat4 camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up)); - batch.setViewTransform(camera); + batch.setViewTransform(_camera); batch.setPipeline(_pipeline); batch.setModelTransform(Transform()); - auto geometryCache = DependencyManager::get(); - - // Render grid on xz plane (not the optimal way to do things, but w/e) - // Note: GeometryCache::renderGrid will *not* work, as it is apparenly unaffected by batch rotations and renders xy only - { - static const std::string GRID_INSTANCE = "Grid"; - static auto compactColor1 = toCompactColor(vec4{ 0.35f, 0.25f, 0.15f, 1.0f }); - static auto compactColor2 = toCompactColor(vec4{ 0.15f, 0.25f, 0.35f, 1.0f }); - static std::vector transforms; - static gpu::BufferPointer colorBuffer; - if (!transforms.empty()) { - transforms.reserve(200); - colorBuffer = std::make_shared(); - for (int i = 0; i < 100; ++i) { - { - glm::mat4 transform = glm::translate(mat4(), vec3(0, -1, -50 + i)); - transform = glm::scale(transform, vec3(100, 1, 1)); - transforms.push_back(transform); - colorBuffer->append(compactColor1); - } - - { - glm::mat4 transform = glm::mat4_cast(quat(vec3(0, PI / 2.0f, 0))); - transform = glm::translate(transform, vec3(0, -1, -50 + i)); - transform = glm::scale(transform, vec3(100, 1, 1)); - transforms.push_back(transform); - colorBuffer->append(compactColor2); - } - } - } - - auto pipeline = geometryCache->getSimplePipeline(); - for (auto& transform : transforms) { - batch.setModelTransform(transform); - batch.setupNamedCalls(GRID_INSTANCE, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - batch.setViewTransform(camera); - batch.setPipeline(_pipeline); - geometryCache->renderWireShapeInstances(batch, GeometryCache::Line, data.count(), colorBuffer); - }); - } - } - - { - static const size_t ITEM_COUNT = 1000; - static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT; - static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT; - - static const gpu::Element POSITION_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ }; - static const gpu::Element NORMAL_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ }; - static const gpu::Element COLOR_ELEMENT{ gpu::VEC4, gpu::NUINT8, gpu::RGBA }; - static const gpu::Element TRANSFORM_ELEMENT{ gpu::MAT4, gpu::FLOAT, gpu::XYZW }; - - - static std::vector transforms; - static std::vector colors; - static gpu::BufferPointer indirectBuffer; - static gpu::BufferPointer transformBuffer; - static gpu::BufferPointer colorBuffer; - static gpu::BufferView colorView; - static gpu::BufferView instanceXfmView; - - if (!transformBuffer) { - transformBuffer = std::make_shared(); - colorBuffer = std::make_shared(); - indirectBuffer = std::make_shared(); - - static const float ITEM_RADIUS = 20; - static const vec3 ITEM_TRANSLATION{ 0, 0, -ITEM_RADIUS }; - for (size_t i = 0; i < TYPE_COUNT; ++i) { - GeometryCache::Shape shape = SHAPE[i]; - GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape]; - { - gpu::Batch::DrawIndexedIndirectCommand indirectCommand; - indirectCommand._count = (uint)shapeData._indexCount; - indirectCommand._instanceCount = ITEM_COUNT; - indirectCommand._baseInstance = (uint)(i * ITEM_COUNT); - indirectCommand._firstIndex = (uint)shapeData._indexOffset / 2; - indirectCommand._baseVertex = 0; - indirectBuffer->append(indirectCommand); - } - - //indirectCommand._count - float startingInterval = ITEM_INTERVAL * i; - for (size_t j = 0; j < ITEM_COUNT; ++j) { - float theta = j * SHAPE_INTERVAL + startingInterval; - auto transform = glm::rotate(mat4(), theta, Vectors::UP); - transform = glm::rotate(transform, (randFloat() - 0.5f) * PI / 4.0f, Vectors::UNIT_X); - transform = glm::translate(transform, ITEM_TRANSLATION); - transform = glm::scale(transform, vec3(randFloat() / 2.0f + 0.5f)); - transformBuffer->append(transform); - transforms.push_back(transform); - auto color = vec4{ randomColorValue(64), randomColorValue(64), randomColorValue(64), 255 }; - color /= 255.0f; - colors.push_back(color); - colorBuffer->append(toCompactColor(color)); - } - } - colorView = gpu::BufferView(colorBuffer, COLOR_ELEMENT); - instanceXfmView = gpu::BufferView(transformBuffer, TRANSFORM_ELEMENT); - } - -#if 1 - GeometryCache::ShapeData shapeData = geometryCache->_shapes[GeometryCache::Icosahedron]; - { - batch.setViewTransform(camera); - batch.setModelTransform(Transform()); - batch.setPipeline(_pipeline); - batch.setInputFormat(getInstancedSolidStreamFormat()); - batch.setInputBuffer(gpu::Stream::COLOR, colorView); - batch.setIndirectBuffer(indirectBuffer); - shapeData.setupBatch(batch); - batch.multiDrawIndexedIndirect(TYPE_COUNT, gpu::TRIANGLES); - } -#else - batch.setViewTransform(camera); - batch.setPipeline(_pipeline); - for (size_t i = 0; i < TYPE_COUNT; ++i) { - GeometryCache::Shape shape = SHAPE[i]; - for (size_t j = 0; j < ITEM_COUNT; ++j) { - int index = i * ITEM_COUNT + j; - batch.setModelTransform(transforms[index]); - const vec4& color = colors[index]; - batch._glColor4f(color.r, color.g, color.b, 1.0); - geometryCache->renderShape(batch, shape); - } - } -#endif - } - - // Render unlit cube + sphere - static auto startUsecs = usecTimestampNow(); - float seconds = getSeconds(startUsecs); - - seconds /= 4.0f; - int shapeIndex = ((int)seconds) % TYPE_COUNT; - bool wire = (seconds - floorf(seconds) > 0.5f); - batch.setModelTransform(Transform()); - batch._glColor4f(0.8f, 0.25f, 0.25f, 1.0f); - - if (wire) { - geometryCache->renderWireShape(batch, SHAPE[shapeIndex]); - } else { - geometryCache->renderShape(batch, SHAPE[shapeIndex]); - } - - batch.setModelTransform(Transform().setScale(2.05f)); - batch._glColor4f(1, 1, 1, 1); - geometryCache->renderWireCube(batch); + //drawFloorGrid(batch); + //drawSimpleShapes(batch); + drawCenterShape(batch); + drawTerrain(batch); _context->render(batch); _qGlContext.swapBuffers(this); @@ -387,12 +527,12 @@ protected: int main(int argc, char** argv) { QGuiApplication app(argc, argv); QTestWindow window; - QTimer timer; - timer.setInterval(0); - app.connect(&timer, &QTimer::timeout, &app, [&] { + auto timer = new QTimer(&app); + timer->setInterval(0); + app.connect(timer, &QTimer::timeout, &app, [&] { window.draw(); }); - timer.start(); + timer->start(); app.exec(); return 0; } From c35d81260d70d1545be168be27226a24b384b553 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 15 Apr 2016 00:49:45 -0700 Subject: [PATCH 3/7] Load local texture files through the same code as network --- .../src/model-networking/TextureCache.cpp | 119 +++++++++--------- .../src/model-networking/TextureCache.h | 2 +- 2 files changed, 60 insertions(+), 61 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index e482c20b11..0e93119fe3 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -154,21 +154,63 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type return ResourceCache::getResource(url, QUrl(), content.isEmpty(), &extra).staticCast(); } -/// Returns a texture version of an image file -gpu::TexturePointer TextureCache::getImageTexture(const QString& path) { - QImage image = QImage(path).mirrored(false, true); - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); - if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); - formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA); + +TextureCache::TextureLoaderFunc getTextureLoaderForType(TextureType type) { + switch (type) { + case ALBEDO_TEXTURE: { + return model::TextureUsage::createAlbedoTextureFromImage; + break; + } + case EMISSIVE_TEXTURE: { + return model::TextureUsage::createEmissiveTextureFromImage; + break; + } + case LIGHTMAP_TEXTURE: { + return model::TextureUsage::createLightmapTextureFromImage; + break; + } + case CUBE_TEXTURE: { + return model::TextureUsage::createCubeTextureFromImage; + break; + } + case BUMP_TEXTURE: { + return model::TextureUsage::createNormalTextureFromBumpImage; + break; + } + case NORMAL_TEXTURE: { + return model::TextureUsage::createNormalTextureFromNormalImage; + break; + } + case ROUGHNESS_TEXTURE: { + return model::TextureUsage::createRoughnessTextureFromImage; + break; + } + case GLOSS_TEXTURE: { + return model::TextureUsage::createRoughnessTextureFromGlossImage; + break; + } + case SPECULAR_TEXTURE: { + return model::TextureUsage::createMetallicTextureFromImage; + break; + } + case CUSTOM_TEXTURE: { + Q_ASSERT(false); + return TextureCache::TextureLoaderFunc(); + break; + } + case DEFAULT_TEXTURE: + default: { + return model::TextureUsage::create2DTextureFromImage; + break; + } } - gpu::TexturePointer texture = gpu::TexturePointer( - gpu::Texture::create2D(formatGPU, image.width(), image.height(), - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); - texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - texture->autoGenerateMips(-1); - return texture; +} + +/// Returns a texture version of an image file +gpu::TexturePointer TextureCache::getImageTexture(const QString& path, TextureType type) { + QImage image = QImage(path); + auto loader = getTextureLoaderForType(type); + return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString())); } QSharedPointer TextureCache::createResource(const QUrl& url, @@ -203,53 +245,10 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture } NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { - switch (_type) { - case ALBEDO_TEXTURE: { - return TextureLoaderFunc(model::TextureUsage::createAlbedoTextureFromImage); - break; - } - case EMISSIVE_TEXTURE: { - return TextureLoaderFunc(model::TextureUsage::createEmissiveTextureFromImage); - break; - } - case LIGHTMAP_TEXTURE: { - return TextureLoaderFunc(model::TextureUsage::createLightmapTextureFromImage); - break; - } - case CUBE_TEXTURE: { - return TextureLoaderFunc(model::TextureUsage::createCubeTextureFromImage); - break; - } - case BUMP_TEXTURE: { - return TextureLoaderFunc(model::TextureUsage::createNormalTextureFromBumpImage); - break; - } - case NORMAL_TEXTURE: { - return TextureLoaderFunc(model::TextureUsage::createNormalTextureFromNormalImage); - break; - } - case ROUGHNESS_TEXTURE: { - return TextureLoaderFunc(model::TextureUsage::createRoughnessTextureFromImage); - break; - } - case GLOSS_TEXTURE: { - return TextureLoaderFunc(model::TextureUsage::createRoughnessTextureFromGlossImage); - break; - } - case SPECULAR_TEXTURE: { - return TextureLoaderFunc(model::TextureUsage::createMetallicTextureFromImage); - break; - } - case CUSTOM_TEXTURE: { - return _textureLoader; - break; - } - case DEFAULT_TEXTURE: - default: { - return TextureLoaderFunc(model::TextureUsage::create2DTextureFromImage); - break; - } + if (_type == CUSTOM_TEXTURE) { + return _textureLoader; } + return getTextureLoaderForType(_type); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 4fe9a89460..a392117958 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -72,7 +72,7 @@ public: const gpu::TexturePointer& getNormalFittingTexture(); /// Returns a texture version of an image file - static gpu::TexturePointer getImageTexture(const QString& path); + static gpu::TexturePointer getImageTexture(const QString& path, TextureType type = DEFAULT_TEXTURE); /// Loads a texture from the specified URL. NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, From ac3d6c77b9e2574d2d4265a4ba2a60351dc75166 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 15 Apr 2016 00:50:28 -0700 Subject: [PATCH 4/7] Add default parameter to mip loaded notifications --- libraries/gpu/src/gpu/Texture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9dde359596..49885f4f19 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -430,7 +430,7 @@ public: Stamp getSamplerStamp() const { return _samplerStamp; } // Only callable by the Backend - void notifyMipFaceGPULoaded(uint16 level, uint8 face) const { return _storage->notifyMipFaceGPULoaded(level, face); } + void notifyMipFaceGPULoaded(uint16 level, uint8 face = 0) const { return _storage->notifyMipFaceGPULoaded(level, face); } const GPUObjectPointer gpuObject {}; From 0d3bf366c1f8675976f945ef467a7ea23d1a5c23 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 15 Apr 2016 01:02:28 -0700 Subject: [PATCH 5/7] Support loading multiple mips in texture transfer --- libraries/gpu/src/gpu/GLBackend.h | 2 +- libraries/gpu/src/gpu/GLBackendTexture.cpp | 73 +++++++++++++--------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index aabd84fbfb..9806c17db4 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -139,7 +139,7 @@ public: GLuint _virtualSize; // theorical size as expected GLuint _numLevels{ 0 }; - void transferMip(GLenum target, const Texture::PixelsPointer& mip) const; + void transferMip(uint16_t mipLevel, uint8_t face = 0) const; // The owning texture const Texture& _gpuTexture; diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 0514411d7f..dfb854143b 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -52,8 +52,7 @@ GLBackend::GLTexture::GLTexture(const Texture& texture) : _target(gpuToGLTextureType(texture)), _size(0), _virtualSize(0), - _numLevels(1), - //_numLevels(texture.maxMip() + 1), + _numLevels(texture.maxMip() + 1), _gpuTexture(texture) { Backend::incrementTextureGPUCount(); @@ -94,25 +93,22 @@ void GLBackend::GLTexture::createTexture() { (void)CHECK_GL_ERROR(); // Fixme: this usage of TexStorage doesn;t work wtih compressed texture, altuogh it should. // GO through the process of allocating the correct storage - /* if (GLEW_VERSION_4_2 && !texture.getTexelFormat().isCompressed()) { - glTexStorage2D(_target, _numLevels, texelFormat.internalFormat, width, height); - (void)CHECK_GL_ERROR(); - } else*/ - { + if (GLEW_VERSION_4_2 && !_gpuTexture.getTexelFormat().isCompressed()) { + glTexStorage2D(_target, _numLevels, texelFormat.internalFormat, width, height); + (void)CHECK_GL_ERROR(); + } else { glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _numLevels - 1); - - // for (int l = 0; l < _numLevels; l++) { - { int l = 0; - if (_gpuTexture.getType() == gpu::Texture::TEX_CUBE) { - for (size_t face = 0; face < CUBE_NUM_FACES; face++) { - glTexImage2D(CUBE_FACE_LAYOUT[face], l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL); + for (uint16_t l = 0; l < _numLevels; l++) { + if (_gpuTexture.getType() == gpu::Texture::TEX_CUBE) { + for (size_t face = 0; face < CUBE_NUM_FACES; face++) { + glTexImage2D(CUBE_FACE_LAYOUT[face], l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL); + } + } else { + glTexImage2D(_target, l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL); } - } else { - glTexImage2D(_target, l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL); - } - width = std::max(1, (width / 2)); - height = std::max(1, (height / 2)); + width = std::max(1, (width / 2)); + height = std::max(1, (height / 2)); } (void)CHECK_GL_ERROR(); } @@ -214,9 +210,13 @@ bool GLBackend::GLTexture::isReady() const { } // Move content bits from the CPU to the GPU for a given mip / face -void GLBackend::GLTexture::transferMip(GLenum target, const Texture::PixelsPointer& mip) const { +void GLBackend::GLTexture::transferMip(uint16_t mipLevel, uint8_t face) const { + auto mip = _gpuTexture.accessStoredMipFace(mipLevel, face); GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat(), mip->getFormat()); - glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData()); + GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face]; + uvec2 size = uvec2(_gpuTexture.getWidth(), _gpuTexture.getHeight()); + size >>= mipLevel; + glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); (void)CHECK_GL_ERROR(); } @@ -235,16 +235,20 @@ void GLBackend::GLTexture::transfer() const { // GO through the process of allocating the correct storage and/or update the content switch (_gpuTexture.getType()) { case Texture::TEX_2D: - if (_gpuTexture.isStoredMipFaceAvailable(0)) { - transferMip(GL_TEXTURE_2D, _gpuTexture.accessStoredMipFace(0)); + for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + if (_gpuTexture.isStoredMipFaceAvailable(i)) { + transferMip(i); + } } break; case Texture::TEX_CUBE: // transfer pixels from each faces for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) { - if (_gpuTexture.isStoredMipFaceAvailable(0, f)) { - transferMip(CUBE_FACE_LAYOUT[f], _gpuTexture.accessStoredMipFace(0, f)); + for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + if (_gpuTexture.isStoredMipFaceAvailable(i, f)) { + transferMip(i, f); + } } } break; @@ -255,7 +259,7 @@ void GLBackend::GLTexture::transfer() const { } if (_gpuTexture.isAutogenerateMips()) { - //glGenerateMipmap(_target); + glGenerateMipmap(_target); (void)CHECK_GL_ERROR(); } } @@ -270,12 +274,21 @@ void GLBackend::GLTexture::postTransfer() { // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory switch (_gpuTexture.getType()) { case Texture::TEX_2D: - _gpuTexture.notifyMipFaceGPULoaded(0, 0); + for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + if (_gpuTexture.isStoredMipFaceAvailable(i)) { + _gpuTexture.notifyMipFaceGPULoaded(i); + } + } break; case Texture::TEX_CUBE: - for (uint8_t f = 0; f < CUBE_NUM_FACES; ++f) { - _gpuTexture.notifyMipFaceGPULoaded(0, f); + // transfer pixels from each faces + for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) { + for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + if (_gpuTexture.isStoredMipFaceAvailable(i, f)) { + _gpuTexture.notifyMipFaceGPULoaded(i, f); + } + } } break; @@ -346,7 +359,7 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) { } else { object = Backend::getGPUObject(*texture); } - if (object) { + if (object && object->getSyncState() == GLTexture::Idle) { return object->_texture; } else { return 0; @@ -436,7 +449,7 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { glActiveTexture(GL_TEXTURE0 + bindingSlot); glBindTexture(object->_target, object->_texture); - //glGenerateMipmap(object->_target); + glGenerateMipmap(object->_target); if (freeSlot < 0) { // If had to use slot 0 then restore state From 3a969eed476916a4ccd37bc4e81d0a9932801f2b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 15 Apr 2016 01:03:22 -0700 Subject: [PATCH 6/7] Manually generate image mip levels when loading textures --- libraries/model/src/model/TextureMap.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 1ef8d57945..7200793bed 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -150,8 +150,8 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask); gpu::Texture* theTexture = nullptr; - if ((image.width() > 0) && (image.height() > 0)) { + if ((image.width() > 0) && (image.height() > 0)) { gpu::Element formatGPU; gpu::Element formatMip; defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress); @@ -171,6 +171,14 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag if (generateMips) { theTexture->autoGenerateMips(-1); + auto levels = theTexture->maxMip(); + uvec2 size(image.width(), image.height()); + for (uint8_t i = 1; i <= levels; ++i) { + size >>= 1; + size = glm::max(size, uvec2(1)); + image = image.scaled(size.x, size.y, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + theTexture->assignStoredMip(i, formatMip, image.byteCount(), image.constBits()); + } } } @@ -291,7 +299,6 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); theTexture->autoGenerateMips(-1); From b50f835d3515508af57927e2fe037a30d180e7ef Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 15 Apr 2016 01:43:09 -0700 Subject: [PATCH 7/7] Warning and error fixes --- interface/src/FrameTimingsScriptingInterface.cpp | 8 ++++---- libraries/gl/src/gl/OglplusHelpers.cpp | 2 +- tests/gpu-test/src/main.cpp | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/src/FrameTimingsScriptingInterface.cpp b/interface/src/FrameTimingsScriptingInterface.cpp index 6c8c164804..59b653105c 100644 --- a/interface/src/FrameTimingsScriptingInterface.cpp +++ b/interface/src/FrameTimingsScriptingInterface.cpp @@ -29,7 +29,7 @@ void FrameTimingsScriptingInterface::finish() { _min = std::numeric_limits::max(); _max = std::numeric_limits::lowest(); size_t count = _values.size(); - for (auto i = 0; i < count; ++i) { + for (size_t i = 0; i < count; ++i) { const uint64_t& value = _values[i]; _max = std::max(_max, value); _min = std::min(_min, value); @@ -37,7 +37,7 @@ void FrameTimingsScriptingInterface::finish() { } _mean = (float)total / (float)count; float deviationTotal = 0; - for (auto i = 0; i < count; ++i) { + for (size_t i = 0; i < count; ++i) { float deviation = _values[i] - _mean; deviationTotal += deviation*deviation; } @@ -46,8 +46,8 @@ void FrameTimingsScriptingInterface::finish() { QVariantList FrameTimingsScriptingInterface::getValues() const { QVariantList result; - for (const auto& v : _values) { - result << v; + for (quint64 v : _values) { + result << QVariant(v); } return result; } diff --git a/libraries/gl/src/gl/OglplusHelpers.cpp b/libraries/gl/src/gl/OglplusHelpers.cpp index 11c4f2fe3d..220ea86646 100644 --- a/libraries/gl/src/gl/OglplusHelpers.cpp +++ b/libraries/gl/src/gl/OglplusHelpers.cpp @@ -126,7 +126,7 @@ ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect) { } ShapeWrapperPtr loadSkybox(ProgramPtr program) { - return ShapeWrapperPtr(new shapes::ShapeWrapper({ { "Position" } }, shapes::SkyBox(), *program)); + return ShapeWrapperPtr(new shapes::ShapeWrapper(std::initializer_list{ "Position" }, shapes::SkyBox(), *program)); } // Return a point's cartesian coordinates on a sphere from pitch and yaw diff --git a/tests/gpu-test/src/main.cpp b/tests/gpu-test/src/main.cpp index 1bb02b8101..47852104a2 100644 --- a/tests/gpu-test/src/main.cpp +++ b/tests/gpu-test/src/main.cpp @@ -422,11 +422,10 @@ public: static const uint SHAPE_TEXTURES_OFFSET = sizeof(glm::vec4); static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element TEXTURE_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV }; - static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT16; - static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint16); std::vector vertices; const int MINX = -1000; const int MAXX = 1000; + // top vertices.push_back(vec4(MAXX, 0, MAXX, 1)); vertices.push_back(vec4(MAXX, MAXX, 0, 0)); @@ -456,7 +455,6 @@ public: pipeline = gpu::Pipeline::create(shader, state); vertexFormat->setAttribute(gpu::Stream::POSITION); vertexFormat->setAttribute(gpu::Stream::TEXCOORD); - }); batch.setPipeline(pipeline); batch.setInputBuffer(gpu::Stream::POSITION, positionView);