2013-07-29 06:08:34 +08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Utility functions that depend on Win32. Notably, our memory allocation;
|
|
|
|
// we use two separate allocators, one for long-lived stuff and one for
|
|
|
|
// stuff that gets freed after every regeneration of the model, to save us
|
|
|
|
// the trouble of freeing the latter explicitly.
|
|
|
|
//
|
|
|
|
// Copyright 2008-2013 Jonathan Westhues.
|
|
|
|
//-----------------------------------------------------------------------------
|
2009-04-20 15:30:09 +08:00
|
|
|
#include "solvespace.h"
|
|
|
|
|
Abstract all (ex-OpenGL) drawing operations into a Canvas interface.
This has several desirable consequences:
* It is now possible to port SolveSpace to a later version of
OpenGL, such as OpenGLES 2, so that it runs on platforms that
only have that OpenGL version;
* The majority of geometry is now rendered without references to
the camera in C++ code, so a renderer can now submit it to
the video card once and re-rasterize with a different projection
matrix every time the projection is changed, avoiding expensive
reuploads;
* The DOGD (draw or get distance) interface is now
a straightforward Canvas implementation;
* There are no more direct references to SS.GW.(projection)
in sketch rendering code, which allows rendering to multiple
viewports;
* There are no more unnecessary framebuffer flips on CPU on Cocoa
and GTK;
* The platform-dependent GL code is now confined to rendergl1.cpp.
* The Microsoft and Apple headers required by it that are prone to
identifier conflicts are no longer included globally;
* The rendergl1.cpp implementation can now be omitted from
compilation to run SolveSpace headless or with a different
OpenGL version.
Note these implementation details of Canvas:
* GetCamera currently always returns a reference to the field
`Camera camera;`. This is so that a future renderer that caches
geometry in the video memory can define it as asserting, which
would provide assurance against code that could accidentally
put something projection-dependent in the cache;
* Line and triangle rendering is specified through a level of
indirection, hStroke and hFill. This is so that a future renderer
that batches geometry could cheaply group identical styles.
* DrawPixmap and DrawVectorText accept a (o,u,v) and not a matrix.
This is so that a future renderer into an output format that
uses 2d transforms (e.g. SVG) could easily derive those.
Some additional internal changes were required to enable this:
* Pixmap is now always passed as std::shared_ptr<{const ,}Pixmap>.
This is so that the renderer could cache uploaded textures
between API calls, which requires it to capture a (weak)
reference.
* The PlatformPathEqual function was properly extracted into
platform-specific code. This is so that the <windows.h> header
could be included only where needed (in platform/w32* as well
as rendergl1.cpp).
* The SBsp{2,3}::DebugDraw functions were removed. They can be
rewritten using the Canvas API if they are ever needed.
While no visual changes were originally intended, some minor fixes
happened anyway:
* The "emphasis" yellow line from top-left corner is now correctly
rendered much wider.
* The marquee rectangle is now pixel grid aligned.
* The hidden entities now do not clobber the depth buffer, removing
some minor artifacts.
* The workplane "tab" now scales with the font used to render
the workplane name.
* The workplane name font is now taken from the normals style.
* Workplane and constraint line stipple is insignificantly
different. This is so that it can reuse the existing stipple
codepaths; rendering of workplanes and constraints predates
those.
Some debug functionality was added:
* In graphics window, an fps counter that becomes red when
rendering under 60fps is drawn.
2016-05-31 08:55:13 +08:00
|
|
|
// Include after solvespace.h to avoid identifier clashes.
|
|
|
|
#include <windows.h>
|
|
|
|
|
2015-03-24 01:49:04 +08:00
|
|
|
namespace SolveSpace {
|
2009-04-20 15:30:09 +08:00
|
|
|
static HANDLE PermHeap, TempHeap;
|
|
|
|
|
2013-08-27 02:58:35 +08:00
|
|
|
void dbp(const char *str, ...)
|
2009-04-20 15:30:09 +08:00
|
|
|
{
|
|
|
|
va_list f;
|
|
|
|
static char buf[1024*50];
|
|
|
|
va_start(f, str);
|
|
|
|
_vsnprintf(buf, sizeof(buf), str, f);
|
|
|
|
va_end(f);
|
|
|
|
|
2015-12-27 15:51:28 +08:00
|
|
|
// The native version of OutputDebugString, unlike most others,
|
|
|
|
// is OutputDebugStringA.
|
|
|
|
OutputDebugStringA(buf);
|
2016-11-16 13:55:07 +08:00
|
|
|
OutputDebugStringA("\n");
|
2016-08-01 08:46:09 +08:00
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
// Duplicate to stderr in debug builds, but not in release; this is slow.
|
|
|
|
fputs(buf, stderr);
|
|
|
|
fputc('\n', stderr);
|
|
|
|
#endif
|
2015-12-27 15:51:28 +08:00
|
|
|
}
|
|
|
|
|
2016-05-19 03:38:17 +08:00
|
|
|
void assert_failure(const char *file, unsigned line, const char *function,
|
|
|
|
const char *condition, const char *message) {
|
2016-08-01 08:46:09 +08:00
|
|
|
dbp("File %s, line %u, function %s:", file, line, function);
|
|
|
|
dbp("Assertion '%s' failed: ((%s) == false).", message, condition);
|
2016-05-19 03:38:17 +08:00
|
|
|
#ifdef NDEBUG
|
|
|
|
_exit(1);
|
|
|
|
#else
|
|
|
|
abort();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-12-27 15:51:28 +08:00
|
|
|
std::string Narrow(const wchar_t *in)
|
|
|
|
{
|
|
|
|
std::string out;
|
|
|
|
DWORD len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
|
|
|
|
out.resize(len - 1);
|
2016-05-19 06:51:36 +08:00
|
|
|
ssassert(WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], len, NULL, NULL),
|
|
|
|
"Invalid UTF-16");
|
2015-12-27 15:51:28 +08:00
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Narrow(const std::wstring &in)
|
|
|
|
{
|
|
|
|
if(in == L"") return "";
|
|
|
|
|
|
|
|
std::string out;
|
|
|
|
out.resize(WideCharToMultiByte(CP_UTF8, 0, &in[0], in.length(), NULL, 0, NULL, NULL));
|
2016-05-19 06:51:36 +08:00
|
|
|
ssassert(WideCharToMultiByte(CP_UTF8, 0, &in[0], in.length(),
|
|
|
|
&out[0], out.length(), NULL, NULL),
|
|
|
|
"Invalid UTF-16");
|
2015-12-27 15:51:28 +08:00
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::wstring Widen(const char *in)
|
|
|
|
{
|
|
|
|
std::wstring out;
|
|
|
|
DWORD len = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0);
|
|
|
|
out.resize(len - 1);
|
2016-05-19 06:51:36 +08:00
|
|
|
ssassert(MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], len),
|
|
|
|
"Invalid UTF-8");
|
2015-12-27 15:51:28 +08:00
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::wstring Widen(const std::string &in)
|
|
|
|
{
|
|
|
|
if(in == "") return L"";
|
|
|
|
|
|
|
|
std::wstring out;
|
|
|
|
out.resize(MultiByteToWideChar(CP_UTF8, 0, &in[0], in.length(), NULL, 0));
|
2016-05-19 06:51:36 +08:00
|
|
|
ssassert(MultiByteToWideChar(CP_UTF8, 0, &in[0], in.length(), &out[0], out.length()),
|
|
|
|
"Invalid UTF-8");
|
2015-12-27 15:51:28 +08:00
|
|
|
return out;
|
2009-04-20 15:30:09 +08:00
|
|
|
}
|
|
|
|
|
Abstract all (ex-OpenGL) drawing operations into a Canvas interface.
This has several desirable consequences:
* It is now possible to port SolveSpace to a later version of
OpenGL, such as OpenGLES 2, so that it runs on platforms that
only have that OpenGL version;
* The majority of geometry is now rendered without references to
the camera in C++ code, so a renderer can now submit it to
the video card once and re-rasterize with a different projection
matrix every time the projection is changed, avoiding expensive
reuploads;
* The DOGD (draw or get distance) interface is now
a straightforward Canvas implementation;
* There are no more direct references to SS.GW.(projection)
in sketch rendering code, which allows rendering to multiple
viewports;
* There are no more unnecessary framebuffer flips on CPU on Cocoa
and GTK;
* The platform-dependent GL code is now confined to rendergl1.cpp.
* The Microsoft and Apple headers required by it that are prone to
identifier conflicts are no longer included globally;
* The rendergl1.cpp implementation can now be omitted from
compilation to run SolveSpace headless or with a different
OpenGL version.
Note these implementation details of Canvas:
* GetCamera currently always returns a reference to the field
`Camera camera;`. This is so that a future renderer that caches
geometry in the video memory can define it as asserting, which
would provide assurance against code that could accidentally
put something projection-dependent in the cache;
* Line and triangle rendering is specified through a level of
indirection, hStroke and hFill. This is so that a future renderer
that batches geometry could cheaply group identical styles.
* DrawPixmap and DrawVectorText accept a (o,u,v) and not a matrix.
This is so that a future renderer into an output format that
uses 2d transforms (e.g. SVG) could easily derive those.
Some additional internal changes were required to enable this:
* Pixmap is now always passed as std::shared_ptr<{const ,}Pixmap>.
This is so that the renderer could cache uploaded textures
between API calls, which requires it to capture a (weak)
reference.
* The PlatformPathEqual function was properly extracted into
platform-specific code. This is so that the <windows.h> header
could be included only where needed (in platform/w32* as well
as rendergl1.cpp).
* The SBsp{2,3}::DebugDraw functions were removed. They can be
rewritten using the Canvas API if they are ever needed.
While no visual changes were originally intended, some minor fixes
happened anyway:
* The "emphasis" yellow line from top-left corner is now correctly
rendered much wider.
* The marquee rectangle is now pixel grid aligned.
* The hidden entities now do not clobber the depth buffer, removing
some minor artifacts.
* The workplane "tab" now scales with the font used to render
the workplane name.
* The workplane name font is now taken from the normals style.
* Workplane and constraint line stipple is insignificantly
different. This is so that it can reuse the existing stipple
codepaths; rendering of workplanes and constraints predates
those.
Some debug functionality was added:
* In graphics window, an fps counter that becomes red when
rendering under 60fps is drawn.
2016-05-31 08:55:13 +08:00
|
|
|
bool PathEqual(const std::string &a, const std::string &b)
|
|
|
|
{
|
|
|
|
// Case-sensitivity is actually per-volume on Windows,
|
|
|
|
// but it is tedious to implement and test for little benefit.
|
|
|
|
std::wstring wa = Widen(a), wb = Widen(b);
|
|
|
|
return std::equal(wa.begin(), wa.end(), wb.begin(), /*wb.end(),*/
|
|
|
|
[](wchar_t wca, wchar_t wcb) { return towlower(wca) == towlower(wcb); });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-07-26 03:37:48 +08:00
|
|
|
std::string PathSepPlatformToUnix(const std::string &filename)
|
|
|
|
{
|
|
|
|
std::string result = filename;
|
|
|
|
for(size_t i = 0; i < result.length(); i++) {
|
|
|
|
if(result[i] == '\\')
|
|
|
|
result[i] = '/';
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string PathSepUnixToPlatform(const std::string &filename)
|
|
|
|
{
|
|
|
|
std::string result = filename;
|
|
|
|
for(size_t i = 0; i < result.length(); i++) {
|
|
|
|
if(result[i] == '/')
|
|
|
|
result[i] = '\\';
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-10-09 22:58:07 +08:00
|
|
|
static std::string MakeUNCFilename(const std::string &filename)
|
2015-12-27 16:09:00 +08:00
|
|
|
{
|
|
|
|
// Prepend \\?\ UNC prefix unless already an UNC path.
|
|
|
|
// We never try to fopen paths that are not absolute or
|
|
|
|
// contain separators inappropriate for the platform;
|
|
|
|
// thus, it is always safe to prepend this prefix.
|
|
|
|
std::string uncFilename = filename;
|
|
|
|
if(uncFilename.substr(0, 2) != "\\\\")
|
|
|
|
uncFilename = "\\\\?\\" + uncFilename;
|
2016-10-09 22:58:07 +08:00
|
|
|
return uncFilename;
|
|
|
|
}
|
2015-12-27 16:09:00 +08:00
|
|
|
|
2016-10-09 22:58:07 +08:00
|
|
|
FILE *ssfopen(const std::string &filename, const char *mode)
|
|
|
|
{
|
|
|
|
ssassert(filename.length() == strlen(filename.c_str()),
|
|
|
|
"Unexpected null byte in middle of a path");
|
|
|
|
return _wfopen(Widen(MakeUNCFilename(filename)).c_str(), Widen(mode).c_str());
|
|
|
|
}
|
|
|
|
|
2015-12-27 16:09:00 +08:00
|
|
|
void ssremove(const std::string &filename)
|
|
|
|
{
|
2016-05-19 06:51:36 +08:00
|
|
|
ssassert(filename.length() == strlen(filename.c_str()),
|
|
|
|
"Unexpected null byte in middle of a path");
|
2015-12-27 16:09:00 +08:00
|
|
|
_wremove(Widen(filename).c_str());
|
|
|
|
}
|
|
|
|
|
2009-04-20 15:30:09 +08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// A separate heap, on which we allocate expressions. Maybe a bit faster,
|
|
|
|
// since no fragmentation issues whatsoever, and it also makes it possible
|
|
|
|
// to be sloppy with our memory management, and just free everything at once
|
|
|
|
// at the end.
|
|
|
|
//-----------------------------------------------------------------------------
|
2013-08-27 04:40:25 +08:00
|
|
|
void *AllocTemporary(size_t n)
|
2009-04-20 15:30:09 +08:00
|
|
|
{
|
|
|
|
void *v = HeapAlloc(TempHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n);
|
2016-05-19 06:51:36 +08:00
|
|
|
ssassert(v != NULL, "Cannot allocate memory");
|
2009-04-20 15:30:09 +08:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
void FreeTemporary(void *p) {
|
|
|
|
HeapFree(TempHeap, HEAP_NO_SERIALIZE, p);
|
|
|
|
}
|
2016-05-05 13:54:05 +08:00
|
|
|
void FreeAllTemporary()
|
2009-04-20 15:30:09 +08:00
|
|
|
{
|
|
|
|
if(TempHeap) HeapDestroy(TempHeap);
|
|
|
|
TempHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
|
|
|
|
// This is a good place to validate, because it gets called fairly
|
|
|
|
// often.
|
|
|
|
vl();
|
|
|
|
}
|
|
|
|
|
2013-08-27 04:40:25 +08:00
|
|
|
void *MemAlloc(size_t n) {
|
2009-04-20 15:30:09 +08:00
|
|
|
void *p = HeapAlloc(PermHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n);
|
2016-05-19 06:51:36 +08:00
|
|
|
ssassert(p != NULL, "Cannot allocate memory");
|
2009-04-20 15:30:09 +08:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
void MemFree(void *p) {
|
|
|
|
HeapFree(PermHeap, HEAP_NO_SERIALIZE, p);
|
|
|
|
}
|
|
|
|
|
2016-05-05 13:54:05 +08:00
|
|
|
void vl() {
|
2016-05-19 06:51:36 +08:00
|
|
|
ssassert(HeapValidate(TempHeap, HEAP_NO_SERIALIZE, NULL), "Corrupted heap");
|
|
|
|
ssassert(HeapValidate(PermHeap, HEAP_NO_SERIALIZE, NULL), "Corrupted heap");
|
2009-04-20 15:30:09 +08:00
|
|
|
}
|
|
|
|
|
2016-05-05 13:54:05 +08:00
|
|
|
void InitHeaps() {
|
2009-04-20 15:30:09 +08:00
|
|
|
// Create the heap used for long-lived stuff (that gets freed piecewise).
|
|
|
|
PermHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
|
|
|
|
// Create the heap that we use to store Exprs and other temp stuff.
|
|
|
|
FreeAllTemporary();
|
|
|
|
}
|
2015-11-06 19:11:11 +08:00
|
|
|
}
|