2016-12-24 35 views
1

我想在OSX的單獨線程中做OpenGL渲染。我使用SDL創建窗口,但我想手動編寫OpenGL上下文創建代碼。有時它可以正常工作(它應該在紅色區域顯示綠色方塊),但有時它會顯示白色。OSX的競態條件OpenGL的設置代碼

如果我只運行一個線程(下面有一個#define來打開和關閉),一切正常。如果我插入一個檔位(for循環數到1000萬,另一個#define開關來控制它),它工作正常,這使我相信我有一個競爭條件,並且我需要阻止渲染線程,直到操作系統是做了它所做的一切。

不熟悉Cocoa或Objective-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 

回答

2

首先,你應該熟悉蘋果公司的Thread Safety Summary可可(特別了AppKit)。在那裏,你會發現你不應該從輔助線程操縱視圖層次結構。特別是,撥打-addSubview:是不好的。 (請注意,這裏描述的關於從後臺線程繪製的限制僅適用於傳統繪圖,OpenGL並不要求您將焦點鎖定在視圖上。)

使用@synchronized(native_window)不符合您的想法。它只與同一個對象上明確使用@synchronized()的其他代碼同步。它的確如此,而不是而不是通常只與在該窗口上使用或操作的任何東西同步。我沒有理由相信可可的任何東西都會在它的窗戶上做@synchronized(),所以你沒有任何同步。

由於窗口和上下文的設置是一次性工作,所以它可能只是在輔助線程產生之前在主線程上完成。

其次,您正在創建一個NSOpenGLView並從中獲取上下文,但是您要告訴上下文將自己與不同的視圖關聯(窗口的contentView)。你爲什麼這樣做?該NSOpenGLView擁有該上下文,它應該保持與該視圖相關聯。

最後,你使用的自動調整大小的掩碼似乎很奇怪。由於您允許所有內容都具有靈活性,GL視圖不會與窗口的內容視圖保持同步。它的增長和收縮速度會更慢,其周圍的利潤率會隨着收益率的增加而下降。我猜你只是想NSViewHeightSizable|NSViewWidthSizable,以便意見調整在一起,邊距保持爲0.

+0

「你爲什麼這樣做?因爲我不知道我在做什麼:P您的意見非常感謝。 –

+0

所以我應該: 1.執行GL上下文從主線程 2.擺脫@synchronized 3.創建並讓死一個NSThread讓可可知道我做的事情多線程,可以創建互斥 初始化4.使用share:參數 在渲染線程中創建一個新的GL上下文5.清理我的視圖和我的自動調整大小的東西 –

+0

嗯,實際上,我似乎在主線程上只創建了一個上下文,然後在渲染線程上執行[context makeCurrentContext]。 –