1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2013-2014 Kyle Lutz <kyle.r.lutz@gmail.com>
3 //
4 // Distributed under the Boost Software License, Version 1.0
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt
7 //
8 // See http://boostorg.github.com/compute for more information.
9 //---------------------------------------------------------------------------//
10 
11 #include <iostream>
12 #include <algorithm>
13 
14 #include <GL/gl.h>
15 
16 #include <vtkActor.h>
17 #include <vtkCamera.h>
18 #include <vtkgl.h>
19 #include <vtkInteractorStyleSwitch.h>
20 #include <vtkMapper.h>
21 #include <vtkObjectFactory.h>
22 #include <vtkOpenGLExtensionManager.h>
23 #include <vtkOpenGLRenderWindow.h>
24 #include <vtkProperty.h>
25 #include <vtkRenderer.h>
26 #include <vtkRenderWindow.h>
27 #include <vtkRenderWindowInteractor.h>
28 #include <vtkSmartPointer.h>
29 
30 #include <boost/compute/system.hpp>
31 #include <boost/compute/algorithm/iota.hpp>
32 #include <boost/compute/interop/opengl.hpp>
33 #include <boost/compute/interop/vtk.hpp>
34 #include <boost/compute/utility/dim.hpp>
35 #include <boost/compute/utility/source.hpp>
36 
37 namespace compute = boost::compute;
38 
39 // tesselates a sphere with radius, phi_slices, and theta_slices. returns
40 // a shared opencl/opengl buffer containing the vertex data.
tesselate_sphere(float radius,size_t phi_slices,size_t theta_slices,compute::command_queue & queue)41 compute::opengl_buffer tesselate_sphere(float radius,
42                                         size_t phi_slices,
43                                         size_t theta_slices,
44                                         compute::command_queue &queue)
45 {
46     using compute::dim;
47 
48     const compute::context &context = queue.get_context();
49 
50     const size_t vertex_count = phi_slices * theta_slices;
51 
52     // create opengl buffer
53     GLuint vbo;
54     vtkgl::GenBuffersARB(1, &vbo);
55     vtkgl::BindBufferARB(vtkgl::ARRAY_BUFFER, vbo);
56     vtkgl::BufferDataARB(vtkgl::ARRAY_BUFFER,
57                          sizeof(float) * 4 * vertex_count,
58                          NULL,
59                          vtkgl::STREAM_DRAW);
60     vtkgl::BindBufferARB(vtkgl::ARRAY_BUFFER, 0);
61 
62     // create shared opengl/opencl buffer
63     compute::opengl_buffer vertex_buffer(context, vbo);
64 
65     // tesselate_sphere kernel source
66     const char source[] = BOOST_COMPUTE_STRINGIZE_SOURCE(
67         __kernel void tesselate_sphere(float radius,
68                                        uint phi_slices,
69                                        uint theta_slices,
70                                        __global float4 *vertex_buffer)
71         {
72             const uint phi_i = get_global_id(0);
73             const uint theta_i = get_global_id(1);
74 
75             const float phi = phi_i * 2.f * M_PI_F / phi_slices;
76             const float theta = theta_i * 2.f * M_PI_F / theta_slices;
77 
78             float4 v;
79             v.x = radius * cos(theta) * cos(phi);
80             v.y = radius * cos(theta) * sin(phi);
81             v.z = radius * sin(theta);
82             v.w = 1.f;
83 
84             vertex_buffer[phi_i*phi_slices+theta_i] = v;
85         }
86     );
87 
88     // build tesselate_sphere program
89     compute::program program =
90         compute::program::create_with_source(source, context);
91     program.build();
92 
93     // setup tesselate_sphere kernel
94     compute::kernel kernel(program, "tesselate_sphere");
95     kernel.set_arg<compute::float_>(0, radius);
96     kernel.set_arg<compute::uint_>(1, phi_slices);
97     kernel.set_arg<compute::uint_>(2, theta_slices);
98     kernel.set_arg(3, vertex_buffer);
99 
100     // acqurire buffer so that it is accessible to OpenCL
101     compute::opengl_enqueue_acquire_buffer(vertex_buffer, queue);
102 
103     // execute tesselate_sphere kernel
104     queue.enqueue_nd_range_kernel(
105         kernel, dim(0, 0), dim(phi_slices, theta_slices), dim(1, 1)
106     );
107 
108     // release buffer so that it is accessible to OpenGL
109     compute::opengl_enqueue_release_buffer(vertex_buffer, queue);
110 
111     return vertex_buffer;
112 }
113 
114 // simple vtkMapper subclass to render the tesselated sphere on the gpu.
115 class gpu_sphere_mapper : public vtkMapper
116 {
117 public:
vtkTypeMacro(gpu_sphere_mapper,vtkMapper)118     vtkTypeMacro(gpu_sphere_mapper, vtkMapper)
119 
120     static gpu_sphere_mapper* New()
121     {
122         return new gpu_sphere_mapper;
123     }
124 
Render(vtkRenderer * renderer,vtkActor * actor)125     void Render(vtkRenderer *renderer, vtkActor *actor)
126     {
127         if(!m_initialized){
128             Initialize(renderer, actor);
129             m_initialized = true;
130         }
131 
132         if(!m_tesselated){
133             m_vertex_count = m_phi_slices * m_theta_slices;
134 
135             // tesselate sphere
136             m_vertex_buffer = tesselate_sphere(
137                 m_radius, m_phi_slices, m_theta_slices, m_command_queue
138             );
139 
140             // ensure tesselation is finished (seems to be required on AMD)
141             m_command_queue.finish();
142 
143             // set tesselated flag to true
144             m_tesselated = true;
145         }
146 
147         // draw sphere
148         glEnableClientState(GL_VERTEX_ARRAY);
149         vtkgl::BindBufferARB(vtkgl::ARRAY_BUFFER, m_vertex_buffer.get_opengl_object());
150         glVertexPointer(4, GL_FLOAT, sizeof(float)*4, 0);
151         glDrawArrays(GL_POINTS, 0, m_vertex_count);
152     }
153 
Initialize(vtkRenderer * renderer,vtkActor * actor)154     void Initialize(vtkRenderer *renderer, vtkActor *actor)
155     {
156         // initialize opengl extensions
157         vtkOpenGLExtensionManager *extensions =
158             static_cast<vtkOpenGLRenderWindow *>(renderer->GetRenderWindow())
159                 ->GetExtensionManager();
160         extensions->LoadExtension("GL_ARB_vertex_buffer_object");
161 
162         // initialize opencl/opengl shared context
163         m_context = compute::opengl_create_shared_context();
164         compute::device device = m_context.get_device();
165         std::cout << "device: " << device.name() << std::endl;
166 
167         // create command queue for the gpu device
168         m_command_queue = compute::command_queue(m_context, device);
169     }
170 
GetBounds()171     double* GetBounds()
172     {
173         static double bounds[6];
174         bounds[0] = -m_radius; bounds[1] = m_radius;
175         bounds[2] = -m_radius; bounds[3] = m_radius;
176         bounds[4] = -m_radius; bounds[5] = m_radius;
177         return bounds;
178     }
179 
180 protected:
gpu_sphere_mapper()181     gpu_sphere_mapper()
182     {
183         m_radius = 5.0f;
184         m_phi_slices = 100;
185         m_theta_slices = 100;
186         m_initialized = false;
187         m_tesselated = false;
188     }
189 
190 private:
191     float m_radius;
192     int m_phi_slices;
193     int m_theta_slices;
194     int m_vertex_count;
195     bool m_initialized;
196     bool m_tesselated;
197     compute::context m_context;
198     compute::command_queue m_command_queue;
199     compute::opengl_buffer m_vertex_buffer;
200 };
201 
main(int argc,char * argv[])202 int main(int argc, char *argv[])
203 {
204     // create gpu sphere mapper
205     vtkSmartPointer<gpu_sphere_mapper> mapper =
206         vtkSmartPointer<gpu_sphere_mapper>::New();
207 
208     // create actor for gpu sphere mapper
209     vtkSmartPointer<vtkActor> actor =
210         vtkSmartPointer<vtkActor>::New();
211     actor->GetProperty()->LightingOff();
212     actor->GetProperty()->SetInterpolationToFlat();
213     actor->SetMapper(mapper);
214 
215     // create render window
216     vtkSmartPointer<vtkRenderer> renderer =
217         vtkSmartPointer<vtkRenderer>::New();
218     renderer->SetBackground(.1, .2, .31);
219     vtkSmartPointer<vtkRenderWindow> renderWindow =
220         vtkSmartPointer<vtkRenderWindow>::New();
221     renderWindow->SetSize(800, 600);
222     renderWindow->AddRenderer(renderer);
223     vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
224         vtkSmartPointer<vtkRenderWindowInteractor>::New();
225     vtkInteractorStyleSwitch *interactorStyle =
226         vtkInteractorStyleSwitch::SafeDownCast(
227             renderWindowInteractor->GetInteractorStyle()
228         );
229     interactorStyle->SetCurrentStyleToTrackballCamera();
230     renderWindowInteractor->SetRenderWindow(renderWindow);
231     renderer->AddActor(actor);
232 
233     // render
234     renderer->ResetCamera();
235     vtkCamera *camera = renderer->GetActiveCamera();
236     camera->Elevation(-90.0);
237     renderWindowInteractor->Initialize();
238     renderWindow->Render();
239     renderWindowInteractor->Start();
240 
241     return 0;
242 }
243