Я пытаюсь сделать OpenGL рендеринг в отдельном потоке на OSX. Я использую SDL для создания окна, но я хочу написать код создания контекста OpenGL вручную. Иногда он отлично работает (он должен показывать зеленый квадрат в красном поле), но иногда он просто отображает белый цвет.Состояние гонки в OSX OpenGL код установки
Если я запускаю только один поток (здесь есть #define ниже, чтобы включать и выключать), все работает нормально. Если я вставлю стойло (для цикла, насчитывающего 10 миллионов, другой переключатель #define для управления этим), он отлично работает, что заставляет меня полагать, что у меня есть условие гонки, и что мне нужно блокировать поток рендеринга, пока ОС не будет сделал то, что он сделал.
Не знаком с Какао или Цель-C, как это сделать? Или моя проблема что-то еще?
код следующим образом:
#include </Library/Frameworks/SDL2.framework/Headers/SDL.h>
#include </Library/Frameworks/SDL2.framework/Headers/SDL_syswm.h>
#include <OpenGL/GL3.h>
#include <array>
#import <Cocoa/Cocoa.h>
#include <OpenGL/CGLTypes.h>
#include <OpenGL/OpenGL.h>
#include <OpenGL/CGLRenderers.h>
#include <thread>
namespace
{
float const PositionData[] =
{
-0.5f,-0.5f,0, 0,0,
0.5f,-0.5f,0, 0,0,
0.5f, 0.5f,0, 0,0,
0.5f, 0.5f,0, 0,0,
-0.5f, 0.5f,0, 0,0,
-0.5f,-0.5f,0, 0,0,
};
namespace buffer
{
enum type
{
VERTEX,
TRANSFORM,
MATERIAL,
MAX
};
}//namespace buffer
}//namespace
#define RENDER_THREAD
#define BLOCK_RENDER_THREAD
int main() {
Uint32 init_mode = SDL_INIT_VIDEO | SDL_INIT_TIMER;
#ifdef _DEBUG
init_mode |= SDL_INIT_NOPARACHUTE;
#endif
SDL_Init(init_mode);
Uint32 window_mode = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
SDL_Window* window;
if (NULL == (window = SDL_CreateWindow("Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 500, 500, window_mode))) {
SDL_Quit();
return 1;
}
SDL_SysWMinfo wmi;
SDL_VERSION(&wmi.version);
if (!SDL_GetWindowWMInfo(window, &wmi))
{
return 1;
}
std::atomic<bool> closing(false);
auto PollEventQueue = [&closing]() {
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type)
{
case SDL_QUIT: {
closing = true;
} break;
default: {
} break;
}
}
};
NSWindow* native_window = wmi.info.cocoa.window;
auto RenderThreadMain = [native_window, &closing, PollEventQueue]() {
#ifdef BLOCK_RENDER_THREAD
for (int k = 0; k < 10000000; k++) {}
#endif
NSOpenGLContext* context;
@synchronized (native_window) {
NSOpenGLPixelFormat *pixel_format = nullptr;
NSOpenGLPixelFormatAttribute attributes[64] = {
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAStencilSize, 8,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
NSOpenGLPFANoRecovery,
0
};
NSOpenGLPixelFormatAttribute* the_end = std::find_if(std::begin(attributes), std::end(attributes), [](NSOpenGLPixelFormatAttribute attribute) {
return attribute == 0;
});
if (true) {
NSOpenGLPixelFormatAttribute multisample_attributes[] = {
NSOpenGLPFAMultisample,
NSOpenGLPFASampleBuffers, NSOpenGLPixelFormatAttribute(1),
NSOpenGLPFASamples, NSOpenGLPixelFormatAttribute(4),
0
};
// Copy it onto the attributes array
int k = 0;
while (multisample_attributes[k]) {
*(the_end++) = multisample_attributes[k++];
}
}
NSView* native_view = [native_window contentView];
NSRect native_rect = [native_view bounds];
pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
NSOpenGLView* gl_view = [[NSOpenGLView alloc] initWithFrame:native_rect pixelFormat:pixel_format];
[pixel_format release];
[gl_view setAutoresizingMask:
(NSViewHeightSizable|NSViewWidthSizable|NSViewMinXMargin|NSViewMaxXMargin|NSViewMinYMargin|NSViewMaxYMargin)
];
[native_view addSubview:gl_view];
context = [gl_view openGLContext];
GLint swap_interval = 1;
[context setValues:&swap_interval forParameter:NSOpenGLCPSwapInterval];
[context setView:[native_window contentView]];
[context makeCurrentContext];
}
std::array<GLuint, buffer::MAX> BufferName;
GLuint ProgramName;
GLuint VertexArrayName;
GLint UniformTransform;
GLint UniformMaterial;
const char* vertex_shader =
"#version 150 core\n"
"in vec3 Position;"
"in vec2 UV;"
"void main()"
"{"
" gl_Position = vec4(Position, 1.0);"
"}";
const GLint vertex_shader_length = (GLint)strlen(vertex_shader);
const char* fragment_shader =
"#version 150 core\n"
"out vec4 Color;"
"void main()"
"{"
" Color = vec4(0.0, 1.0, 0.0, 1.0);"
"}";
const GLint fragment_shader_length = (GLint)strlen(fragment_shader);
GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource((GLuint)fragment_shader_id, 1, &fragment_shader, &fragment_shader_length);
glCompileShader((GLuint)fragment_shader_id);
int shader_compiled;
glGetShaderiv((GLuint)fragment_shader_id, GL_COMPILE_STATUS, &shader_compiled);
if (shader_compiled != GL_TRUE) {
int log_length = 0;
char log[1024];
glGetShaderInfoLog((GLuint)fragment_shader_id, 1024, &log_length, log);
printf("%s", log);
return 1; // TODO: Error
}
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
glShaderSource((GLuint)vertex_shader_id, 1, &vertex_shader, &vertex_shader_length);
glCompileShader((GLuint)vertex_shader_id);
glGetShaderiv((GLuint)vertex_shader_id, GL_COMPILE_STATUS, &shader_compiled);
if (shader_compiled != GL_TRUE) {
int log_length = 0;
char log[1024];
glGetShaderInfoLog((GLuint)vertex_shader_id, 1024, &log_length, log);
printf("%s", log);
return 1; // TODO: Error
}
ProgramName = glCreateProgram();
glAttachShader(ProgramName, fragment_shader_id);
glAttachShader(ProgramName, vertex_shader_id);
glBindAttribLocation(ProgramName, 0, "Position");
glLinkProgram(ProgramName);
glGenBuffers(buffer::MAX, &BufferName[0]);
glBindBuffer(GL_ARRAY_BUFFER, BufferName[buffer::VERTEX]);
glBufferData(GL_ARRAY_BUFFER, sizeof(PositionData), PositionData, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenVertexArrays(1, &VertexArrayName);
glBindVertexArray(VertexArrayName);
glBindBuffer(GL_ARRAY_BUFFER, BufferName[buffer::VERTEX]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), 0);
glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (const GLvoid *)(3*sizeof(float)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(4);
glBindVertexArray(0);
while (!closing) {
#ifndef RENDER_THREAD
PollEventQueue();
#endif
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(ProgramName);
glBindVertexArray(VertexArrayName);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 2);
[context flushBuffer];
[context update];
}
return 0;
};
#ifdef RENDER_THREAD
std::thread render_thread = std::thread(RenderThreadMain);
while (!closing) {
PollEventQueue();
}
render_thread.join();
#else
RenderThreadMain();
#endif
return 0;
}
Собран с:
clang++ test.mm -framework OpenGL -framework Cocoa -framework SDL2 -F/Library/Frameworks -std=c++14 -g
«Почему вы это делаете?» Потому что я понятия не имею, что я делаю: P Ваши комментарии очень ценятся. –
Так что я должен: 1. Do GL инициализации контекста от основного потока 2. Избавьтесь от @synchronized 3. Создание и пусть умирает один NSThread так, что какао знает, что я делаю многопоточных вещи и может создавать мьютексы 4. Создайте новый контекст GL в потоке рендеринга, используя параметр share: parameter 5. Очистите свой материал View и мои файлы для автоматического изменения размера –
Хм, на самом деле, похоже, у меня больше успеха, создавая только один контекст в основной теме, а затем выполнив [context makeCurrentContext] в потоке рендеринга. –