Implicit surface rendering via ray tracing
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

591 lines
15 KiB

/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "appwindowprofiler.hpp"
#include <algorithm>
#include <assert.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdarg.h>
#include <stdio.h>
#include "fileoperations.hpp"
#include "misc.hpp"
#include <fileformats/bmp.hpp>
namespace nvh {
static void replace(std::string& str, const std::string& from, const std::string& to)
{
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos)
{
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
}
static void fixDeviceName(std::string& deviceName)
{
replace(deviceName, "INTEL(R) ", "");
replace(deviceName, "AMD ", "");
replace(deviceName, "DRI ", "");
replace(deviceName, "(TM) ", "");
replace(deviceName, " Series", "");
replace(deviceName, " Graphics", "");
replace(deviceName, "/PCIe/SSE2", "");
std::replace(deviceName.begin(), deviceName.end(), ' ', '_');
deviceName.erase(std::remove(deviceName.begin(), deviceName.end(), '/'), deviceName.end());
deviceName.erase(std::remove(deviceName.begin(), deviceName.end(), '\\'), deviceName.end());
deviceName.erase(std::remove(deviceName.begin(), deviceName.end(), ':'), deviceName.end());
deviceName.erase(std::remove(deviceName.begin(), deviceName.end(), '?'), deviceName.end());
deviceName.erase(std::remove(deviceName.begin(), deviceName.end(), '*'), deviceName.end());
deviceName.erase(std::remove(deviceName.begin(), deviceName.end(), '<'), deviceName.end());
deviceName.erase(std::remove(deviceName.begin(), deviceName.end(), '>'), deviceName.end());
deviceName.erase(std::remove(deviceName.begin(), deviceName.end(), '|'), deviceName.end());
deviceName.erase(std::remove(deviceName.begin(), deviceName.end(), '"'), deviceName.end());
deviceName.erase(std::remove(deviceName.begin(), deviceName.end(), ','), deviceName.end());
}
void AppWindowProfiler::onMouseMotion(int x, int y)
{
AppWindowProfiler::WindowState& window = m_windowState;
if(!window.m_mouseButtonFlags && mouse_pos(x, y))
return;
window.m_mouseCurrent[0] = x;
window.m_mouseCurrent[1] = y;
}
void AppWindowProfiler::onMouseButton(MouseButton Button, ButtonAction Action, int mods, int x, int y)
{
AppWindowProfiler::WindowState& window = m_windowState;
m_profiler.reset();
if(mouse_button(Button, Action))
return;
switch(Action)
{
case BUTTON_PRESS: {
switch(Button)
{
case MOUSE_BUTTON_LEFT: {
window.m_mouseButtonFlags |= MOUSE_BUTTONFLAG_LEFT;
}
break;
case MOUSE_BUTTON_MIDDLE: {
window.m_mouseButtonFlags |= MOUSE_BUTTONFLAG_MIDDLE;
}
break;
case MOUSE_BUTTON_RIGHT: {
window.m_mouseButtonFlags |= MOUSE_BUTTONFLAG_RIGHT;
}
break;
}
}
break;
case BUTTON_RELEASE: {
if(!window.m_mouseButtonFlags)
break;
switch(Button)
{
case MOUSE_BUTTON_LEFT: {
window.m_mouseButtonFlags &= ~MOUSE_BUTTONFLAG_LEFT;
}
break;
case MOUSE_BUTTON_MIDDLE: {
window.m_mouseButtonFlags &= ~MOUSE_BUTTONFLAG_MIDDLE;
}
break;
case MOUSE_BUTTON_RIGHT: {
window.m_mouseButtonFlags &= ~MOUSE_BUTTONFLAG_RIGHT;
}
break;
}
}
break;
}
}
void AppWindowProfiler::onMouseWheel(int y)
{
AppWindowProfiler::WindowState& window = m_windowState;
m_profiler.reset();
if(mouse_wheel(y))
return;
window.m_mouseWheel += y;
}
void AppWindowProfiler::onKeyboard(KeyCode key, ButtonAction action, int mods, int x, int y)
{
AppWindowProfiler::WindowState& window = m_windowState;
m_profiler.reset();
if(key_button(key, action, mods))
return;
bool newState = false;
switch(action)
{
case BUTTON_PRESS:
case BUTTON_REPEAT: {
newState = true;
break;
}
case BUTTON_RELEASE: {
newState = false;
break;
}
}
window.m_keyToggled[key] = window.m_keyPressed[key] != newState;
window.m_keyPressed[key] = newState;
}
void AppWindowProfiler::onKeyboardChar(unsigned char key, int mods, int x, int y)
{
m_profiler.reset();
if(key_char(key))
return;
}
void AppWindowProfiler::parseConfigFile(const char* filename)
{
std::string result = loadFile(filename, false);
if(result.empty())
{
LOGW("file not found: %s\n", filename);
return;
}
std::vector<const char*> args;
ParameterList::tokenizeString(result, args);
std::string path = getFilePath(filename);
parseConfig(uint32_t(args.size()), args.data(), path);
}
void AppWindowProfiler::onWindowClose()
{
exitScreenshot();
}
void AppWindowProfiler::onWindowResize(int width, int height)
{
m_profiler.reset();
if(width == 0 || height == 0)
{
return;
}
m_windowState.m_winSize[0] = width;
m_windowState.m_winSize[1] = height;
if(m_activeContext)
{
swapResize(width, height);
}
if(m_active)
{
resize(m_windowState.m_swapSize[0], m_windowState.m_swapSize[1]);
}
}
void AppWindowProfiler::setVsync(bool state)
{
if(m_internal)
{
swapVsync(state);
LOGI("vsync: %s\n", state ? "on" : "off");
}
m_config.vsyncstate = state;
m_vsync = state;
}
int AppWindowProfiler::run(const std::string& title, int argc, const char** argv, int width, int height, bool requireGLContext)
{
m_config.winsize[0] = m_config.winsize[0] ? m_config.winsize[0] : width;
m_config.winsize[1] = m_config.winsize[1] ? m_config.winsize[1] : height;
// skip first argument here (exe file)
parseConfig(argc - 1, argv + 1, ".");
if(!validateConfig())
{
return EXIT_FAILURE;
}
if(!NVPWindow::open(m_config.winpos[0], m_config.winpos[1], m_config.winsize[0], m_config.winsize[1], title.c_str(), requireGLContext))
{
LOGE("Could not create window\n");
return EXIT_FAILURE;
}
m_windowState.m_winSize[0] = m_config.winsize[0];
m_windowState.m_winSize[1] = m_config.winsize[1];
postConfigPreContext();
contextInit();
m_activeContext = true;
// hack to react on $DEVICE$ filename
if(!m_config.logFilename.empty())
{
parameterCallback(m_paramLog);
}
if(contextGetDeviceName())
{
std::string deviceName = contextGetDeviceName();
fixDeviceName(deviceName);
LOGOK("DEVICE: %s\n", deviceName.c_str());
}
initBenchmark();
setVsync(m_config.vsyncstate);
bool Run = begin();
m_active = true;
bool quickExit = m_config.quickexit;
if(m_config.frameLimit)
{
m_profilerPrint = false;
quickExit = true;
}
double timeStart = getTime();
double timeBegin = getTime();
double frames = 0;
bool lastVsync = m_vsync;
m_hadProfilerPrint = false;
double lastProfilerPrintTime = 0;
if(Run)
{
while(pollEvents())
{
bool wasClosed = false;
while(!isOpen())
{
NVPSystem::waitEvents();
wasClosed = true;
}
if(wasClosed)
{
continue;
}
if(m_windowState.onPress(KEY_V))
{
setVsync(!m_vsync);
}
std::string stats;
{
bool benchmarkActive = m_benchmark.sequence.isActive();
double curTime = getTime();
double printInterval = m_profilerPrint && !benchmarkActive ? float(m_config.intervalSeconds) : float(FLT_MAX);
bool printStats = ((curTime - lastProfilerPrintTime) > printInterval);
if(printStats)
{
lastProfilerPrintTime = curTime;
}
m_profiler.beginFrame();
swapPrepare();
{
//const nvh::Profiler::Section profile(m_profiler, "App");
think(getTime() - timeStart);
}
memset(m_windowState.m_keyToggled, 0, sizeof(m_windowState.m_keyToggled));
swapBuffers();
m_profiler.endFrame();
if(printStats)
{
m_profiler.print(stats);
}
}
m_hadProfilerPrint = false;
if(m_profilerPrint && !stats.empty())
{
if(!m_config.timerLimit || m_config.timerLimit == 1)
{
LOGI("%s\n", stats.c_str());
m_hadProfilerPrint = true;
}
if(m_config.timerLimit == 1)
{
m_config.frameLimit = 1;
}
if(m_config.timerLimit)
{
m_config.timerLimit--;
}
}
advanceBenchmark();
postProfiling();
frames++;
double timeCurrent = getTime();
double timeDelta = timeCurrent - timeBegin;
if(timeDelta > double(m_config.intervalSeconds) || lastVsync != m_vsync || m_config.frameLimit == 1)
{
std::ostringstream combined;
if(lastVsync != m_vsync)
{
timeDelta = 0;
}
if(m_timeInTitle)
{
combined << title << ": " << (timeDelta * 1000.0 / (frames)) << " [ms]"
<< (m_vsync ? " (vsync on - V for toggle)" : "");
setTitle(combined.str().c_str());
}
if(m_config.frameLimit == 1)
{
LOGI("frametime: %f ms\n", (timeDelta * 1000.0 / (frames)));
}
frames = 0;
timeBegin = timeCurrent;
lastVsync = m_vsync;
}
if(m_windowState.m_keyPressed[KEY_ESCAPE] || m_config.frameLimit == 1)
break;
if(m_config.frameLimit)
m_config.frameLimit--;
}
}
contextSync();
exitScreenshot();
if(quickExit)
{
exit(EXIT_SUCCESS);
return EXIT_SUCCESS;
}
end();
m_active = false;
contextDeinit();
postEnd();
return Run ? EXIT_SUCCESS : EXIT_FAILURE;
}
void AppWindowProfiler::leave()
{
m_config.frameLimit = 1;
}
std::string AppWindowProfiler::specialStrings(const char* original)
{
std::string str(original);
if(strstr(original, "$DEVICE$"))
{
if(contextGetDeviceName())
{
std::string deviceName = contextGetDeviceName();
fixDeviceName(deviceName);
if(deviceName.empty())
{
// no proper device name available
return std::string();
}
// replace $DEVICE$
replace(str, "$DEVICE$", deviceName);
}
else
{
// no proper device name available
return std::string();
}
}
return str;
}
void AppWindowProfiler::parameterCallback(uint32_t param)
{
if(param == m_paramLog)
{
std::string logfileName = specialStrings(m_config.logFilename.c_str());
if (!logfileName.empty())
{
nvprintSetLogFileName(logfileName.c_str());
}
}
else if(param == m_paramCfg || param == m_paramBat)
{
parseConfigFile(m_config.configFilename.c_str());
}
else if(param == m_paramWinsize)
{
if(m_internal)
{
setWindowSize(m_config.winsize[0], m_config.winsize[1]);
}
}
if(!m_active)
return;
if(param == m_paramVsync)
{
setVsync(m_config.vsyncstate);
}
else if(param == m_paramScreenshot)
{
std::string filename = specialStrings(m_config.screenshotFilename.c_str());
if (!filename.empty())
{
screenshot(filename.c_str());
}
}
else if(param == m_paramClear)
{
clear(m_config.clearColor[0], m_config.clearColor[1], m_config.clearColor[2]);
}
}
void AppWindowProfiler::setupParameters()
{
nvh::ParameterList::Callback callback = [&](uint32_t param) { parameterCallback(param); };
m_paramWinsize = m_parameterList.add("winsize|Set window size (width and height)", m_config.winsize, callback, 2);
m_paramVsync = m_parameterList.add("vsync|Enable or disable vsync", &m_config.vsyncstate, callback);
m_paramLog = m_parameterList.addFilename("logfile|Set logfile", &m_config.logFilename, callback);
m_paramCfg = m_parameterList.addFilename(".cfg|load parameters from this config file", &m_config.configFilename, callback);
m_paramBat = m_parameterList.addFilename(".bat|load parameters from this batch file", &m_config.configFilename, callback);
m_parameterList.add("winpos|Set window position (x and y)", m_config.winpos, nullptr, 2);
m_parameterList.add("frames|Set number of frames to render before exit", &m_config.frameLimit);
m_parameterList.add("timerprints|Set number of timerprints to do, before exit", &m_config.timerLimit);
m_parameterList.add("timerinterval|Set interval of timer prints in seconds", &m_config.intervalSeconds);
m_parameterList.add("bmpatexit|Set file to store a bitmap image of the last frame at exit", &m_config.dumpatexitFilename);
m_parameterList.addFilename("benchmark|Set benchmark filename", &m_benchmark.filename);
m_parameterList.add("benchmarkframes|Set number of benchmarkframes", &m_benchmark.frameLength);
m_parameterList.add("quickexit|skips tear down", &m_config.quickexit);
m_paramScreenshot = m_parameterList.add("screenshot|makes a screenshot into this file", &m_config.screenshotFilename, callback);
m_paramClear = m_parameterList.add("clear|clears window color (r,b,g in 0-255) using OS", m_config.clearColor, callback, 3);
}
void AppWindowProfiler::exitScreenshot()
{
if(!m_config.dumpatexitFilename.empty() && !m_hadScreenshot)
{
screenshot(m_config.dumpatexitFilename.c_str());
m_hadScreenshot = true;
}
}
void AppWindowProfiler::initBenchmark()
{
if(m_benchmark.filename.empty())
return;
m_benchmark.content = loadFile(m_benchmark.filename.c_str(), false);
if(!m_benchmark.content.empty())
{
std::vector<const char*> tokens;
ParameterList::tokenizeString(m_benchmark.content, tokens);
std::string path = getFilePath(m_benchmark.filename.c_str());
m_benchmark.sequence.init(&m_parameterList, tokens);
// do first iteration manually, due to custom arg parsing
uint32_t argBegin;
uint32_t argCount;
if(!m_benchmark.sequence.advanceIteration("benchmark", 1, argBegin, argCount))
{
parseConfig(argCount, &tokens[argBegin], path);
}
m_profiler.reset(nvh::Profiler::CONFIG_DELAY);
m_benchmark.frame = 0;
m_profilerPrint = false;
}
}
void AppWindowProfiler::advanceBenchmark()
{
if(!m_benchmark.sequence.isActive())
return;
m_benchmark.frame++;
if(m_benchmark.frame > m_benchmark.frameLength + nvh::Profiler::CONFIG_DELAY + nvh::Profiler::FRAME_DELAY)
{
m_benchmark.frame = 0;
std::string stats;
m_profiler.print(stats);
LOGI("BENCHMARK %d \"%s\" {\n", m_benchmark.sequence.getIteration(), m_benchmark.sequence.getSeparatorArg(0));
LOGI("%s}\n\n", stats.c_str());
bool done = m_benchmark.sequence.applyIteration("benchmark", 1, "-");
m_profiler.reset(nvh::Profiler::CONFIG_DELAY);
postBenchmarkAdvance();
if(done)
{
leave();
}
}
}
} // namespace nvh