0% found this document useful (0 votes)
16 views9 pages

GPU Programming Final Exam Overview

This document contains the final exam for a GPU programming course. It includes 4 problems testing knowledge of GPU rendering techniques including geometry and fragment shaders. Problem 1 asks to complete code launching a rendering pass with lines instead of triangles and modify shaders to work with it. Problem 2 estimates data transferred to the GPU for a rendering pass and amount read by a vertex shader. Problem 3 implements a geometry shader to render discs and modifies a fragment shader to render a perfect circle. Problem 4 explains the depth test and cases where depth sorting eliminates its need.

Uploaded by

moien
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views9 pages

GPU Programming Final Exam Overview

This document contains the final exam for a GPU programming course. It includes 4 problems testing knowledge of GPU rendering techniques including geometry and fragment shaders. Problem 1 asks to complete code launching a rendering pass with lines instead of triangles and modify shaders to work with it. Problem 2 estimates data transferred to the GPU for a rendering pass and amount read by a vertex shader. Problem 3 implements a geometry shader to render discs and modifies a fragment shader to render a perfect circle. Problem 4 explains the depth test and cases where depth sorting eliminates its need.

Uploaded by

moien
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Name

GPU Programming
EE 4702-1
Final Examination
Tuesday, 5 December 2017 12:30–14:30 CST

Problem 1 (15 pts)

Problem 2 (20 pts)

Problem 3 (30 pts)

Problem 4 (35 pts)

Alias Exam Total (100 pts)

Good Luck!
Problem 1: [15 pts] Appearing below is code based on Homework 5 (the tile cloud). First is code that
launches the rendering passes, followed by the shaders. The shaders below were written to work with the
rendering pass launched by case 1. In case 3, which is incomplete, the rendering pass input primitives are
lines instead of triangles. Complete the case 3 code and modify the shaders to work with your completed
case 3 code.
Complete the case 3 code. Use abbreviations such as glV (for glVertex4f), glC, and glN.
case 1: { pShader_Use use(s_hw05_tiles_1);
glBegin(GL_TRIANGLES);
for ( Tile* tile: tiles ) { glColor4fv(tile->color); glVertex4fv(tile->pt_00);
glColor4fv(tile->tact_pos); glVertex4fv(tile->ax);
glVertex4fv(tile->ay); }
glEnd(); } break;
case 3: { pShader_Use use(s_hw05_tiles_3);
glBegin(GL_LINES); // <--- DON’T FORGET, LINES
for ( Tile* tile: tiles ) {

}
glEnd(); } break;

Modify shader code (below) to work with case 3 (above). Look at type of primitive (above),
interface blocks, shader routines, and layout declarations.
out Data_to_GS { vec4 vertex_o; vec4 color; };

void vs_main_tiles_3() { // Vertex shader routine. // CHANGE/ADD SOMETHING


vertex_o = gl_Vertex;
color = gl_Color;
}

in Data_to_GS { vec4 vertex_o; vec4 color; } In[3]; // CHANGE/ADD SOMETHING

layout ( triangles ) in; // CHANGE/ADD ONE OF


layout ( triangle_strip, max_vertices = 4 ) out; // THE LAYOUT DECLARATIONS

void gs_main_tiles_3() { // Geometry shader routine. // CHANGE/ADD SEVERAL THINGS

vec4 pt_00 = In[0].vertex_o;

vec4 ax_o = vec4(In[1].vertex_o.xyz,0);

vec4 ay_o = vec4(In[2].vertex_o.xyz,0);

vec4 tact_pos = In[1].color;

color = In[0].color;

vec4 vtx_o[4];
vtx_o[0] = pt_00; vtx_o[1] = pt_00 + ax_o;
vtx_o[2] = pt_00 + ay_o; vtx_o[3] = pt_00 + ay_o + ax_o;
// The code below does not need to be changed and so isn’t shown.

2
Problem 2: [20 pts] Appearing below is the render_tiles routine from Homework 5.
(a) Estimate the amount of data sent from the CPU to the GPU for the rendering pass started by the code
below. Use the following symbol: n, the number of tiles.
case 1: {
pShader_Use use(s_hw05_tiles_1);
glUniform2i(1, opt_tryout1, opt_tryout2);
glUniform1i(2, light_state_get());
glUniform1f(3, world_time);

glBegin(GL_TRIANGLES);
for ( Tile* tile: tiles ) {
glColor4fv(tile->color);
glVertex4fv(tile->pt_00);
glColor4fv(tile->tact_pos);
glVertex4fv(tile->ax);
glVertex4fv(tile->ay);
}
glEnd();
}
break;

Amount of data, in bytes:

(b) The vertex shader below is used with the case 1 code above. In terms of n, how much data is read by
this vertex shader for a rendering pass?
void vs_main_tiles_1() {
vertex_o = gl_Vertex;
color = gl_Color;
}

Total data read by shader above for a rendering pass:

Explain why the amount of data read by the vertex shader might be different than the amount of data sent
from CPU to GPU when executing the case 1 code.

3
Problem 2, continued: The rendering pass below provides the same data to the shaders as the one in
the previous part, but there are significant differences.
#define TO_BO(name,num,update) \
glBindBuffer(GL_ARRAY_BUFFER,bos_tiles[num]); \
if ( update ) glBufferData \
(GL_ARRAY_BUFFER, [Link]()*sizeof(name[0]), [Link](), GL_STREAM_DRAW); \
glBindBufferBase(GL_SHADER_STORAGE_BUFFER,num,bos_tiles[num]);

pShader_Use use(s_hw05_tiles_2);
glUniform2i(1, opt_tryout1, opt_tryout2);
glUniform1i(2, light_state_get());
glUniform1f(3, world_time);

TO_BO(pt_00, 1, pt_00_data_stale);
TO_BO(ax, 2, axes_data_stale);
TO_BO(ay, 3, axes_data_stale);
TO_BO(color, 4, color_data_stale);
TO_BO(tact_pos, 5, tact_data_stale);
pt_00_data_stale = axes_data_stale = color_data_stale = tact_data_stale = false;

glDrawArrays(GL_POINTS,0,[Link]());

(c) Explain why the code above would be more efficient when rendering the very first frame and why it might
be more efficient later in the execution, depending on how the tiles are used.

More efficient for the very first frame because:

More efficient later in execution because . . .

This efficiency can be assumed by use of variable such as . . . because . . .

4
Problem 3: [30 pts] The code in this problem is similar to the Homework
5 tile code except that it will be used to render discs.
(a) Complete the geometry shader below so that it emits primitives for a
disc (a filled-in circle) writing the geometry shader outputs shown. The ctr_o ax_o
(0.0, 0.5)
provided shader code reads the shader disc center, ctr_o, disc normal, (0.5, 0.5)
nz_o, and a vector to a point on the circumference, ax_o (see diagram),
all in object space. The shader output includes tcoord, a texture coor- texture
coordinates
dinate. Assign this consistent with the texture coordinates shown on the
diagram. Assume that the shader output primitive, a triangle fan, will (0.5, 0.0)
work correctly even though it is not included in OpenGL Shading Lan-
guage 4.5. A triangle fan makes the solution simpler. Note: This triangle
fan discussion was intentionally omitted from the original exam.

Emit primitives for the disc, make sure it’s filled in.

Don’t forget to: Set max vertices, set normal e, set tcoord,
and of course set gl Position.

out Data_to_FS { flat vec3 normal_e; vec3 vertex_e; vec2 tcoord;};

layout ( points ) in;


layout ( triangle_fan, max_vertices = ) out; // <--- FILL IN

void gs_main_disc() {
int vertex_id = In[0].vertex_id;
vec4 ctr_o = ctrs[vertex_id];
vec3 ax_o = axs[vertex_id].xyz;
vec3 nz_o = nzs[vertex_id].xyz;

const int slices = 10;


const float pi = 3.1415926536;
const float delta_theta = 2 * pi / slices;
// Abbrevs: glmvp, gl_ModelViewProjectionMatrix; glmv, gl_ModelViewMatrix; gln, gl_NormalMatrix

for ( int i=0; i<slices; i++ )


{
float theta = i * delta_theta;
float costh = cos(theta);
float sinth = sin(theta);

}
}

5
Problem 3, continued:
(b) Appearing below is the fragment shader for the code above. If variable nevermind were true the fragment
shader would not write a fragment, but it’s set to false in the code. (The discard keyword returns from
the fragment shader without writing a fragment.)
The primitives emitted by the geometry shader (if solved correctly) will render a 10-sided polygon, which is
not exactly a disc (circle). Modify the code so that it emits a perfect disc based on the largest circle that
can fit inside the polygon. (For partial credit, a circle of radius 0.4 in texture coordinate units.) Use texture
coordinates to determine whether a fragment is in the circle.

Assign radius the correct value in terms of slices.

Set nevermind so that a fragment is discarded if it’s outside a radius-radius circle based on tcoord.
void fs_main_disc() {
const int slices = 10;
const float pi = 3.1415926535;
const float delta_theta = 2 * pi / slices;

float radius = 0.4; // CHANGE TO CORRECT VALUE.

const bool nevermind = false; // CHANGE FOR PERFECT CIRCLE

if ( nevermind ) discard; // Don’t write fragment, exit the shader.

vec4 texel = texture(tex_unit_0,tcoord); // NO NEED TO CHANGE THIS CODE.


vec4 color = colors[vertex_id];
gl_FragColor = texel * generic_lighting(vertex_e, color, normal_e);
gl_FragDepth = gl_FragCoord.z;
}

(c) The inputs to fragment shader fs_main_disc appear below. Explain the implications of moving the flat
qualifier as described below.

in Data_to_FS { flat vec3 normal_e; flat int vertex_id;


vec3 vertex_e; vec2 tcoord; };

Explain impact on correctness and efficiency if flat were removed from normal e and vertex id.

Explain impact on correctness and efficiency if flat were added to vertex e and tcoord.

6
Problem 4: [35 pts] Answer each question below.
(a) Explain how the depth (z-buffer) test is used. Provide an example in which sorting primitives by eye
distance makes the depth test unnecessary, and another example in which even with sorting a depth test is
necessary for proper rendering.

Explain how depth test used.

Example where sorting makes depth test unnecessary. Hint: Example can have two primitives.

Example where depth test necessary even with sorting. Hint: Example can have three primitives.

(b) Appearing below are sample uses of two procedures related to the stencil buffer. Explain what each one
does in general (not necessarily in the example).
glStencilFunc(GL_EQUAL,4,-1);
glStencilOp(GL_REPLACE,GL_KEEP,GL_KEEP);

The glStencilFunc procedure is used to . . ..

The glStencilOp procedure is used to . . ..

7
(c) In general, why doesn’t it make sense to access a texture in a vertex shader?

Bad idea to access a texture in a vertex shader because . . .

(d) How is eye space defined? Describe the transformations that need to be applied to map from object
space to eye space. Illustrate your answer using a sample scene.

Defining features of eye space are . . ..

In the following scene to transform from object to eye space the modelview matrix is used, which . . .

8
(e) An NVIDIA GPU has 10 SMs. Consider a kernel which evenly divides work among its threads, the
usual assumption made in class. Further assume that there is a large amount of work. Let t(G) denote the
execution time when the kernel is launched with G blocks. In all cases the block size is 1024 threads.
Let a = t(10), the time when launched with 10 blocks on the 10-SM GPU.

Find an expression for t(5) in terms of a.

Find an expression for t(15) in terms of a.

Find an expression for t(20) in terms of a.

(f) Draw a sketch showing the shadow volume corresponding to a triangle. Include the triangle, the light
source, some object in the shadow and some object seen through the shadow.

Show: the light, triangle, shadow volume for the triangle, shaded object, object
seen through shade.

Common questions

Powered by AI

To render a perfect disc instead of a polygon, the geometry shader should calculate and use more fine-grained angular divisions to approximate a circle more closely. Specifically, by increasing slices and adjusting the texture coordinates accordingly, the disc's continuity can be enhanced. Additionally, within the fragment shader, calculations based on texture coordinates should evaluate if a fragment lies within the inscribed circle, adjusting the 'nevermind' variable to discard fragments outside the computed radius accordingly, ensuring only portions of the polygon that fit within the perfect circle are drawn .

Changing the shader layout declaration from triangles to lines alters how the rendering pipeline processes vertices into geometrical shapes. Triangles are typically used to form 3D surfaces, while lines are processed for wireframe or edge detection effects. For the shader code, this means adjusting primitive input interpretation and modifying shader routines to correctly handle the new geometric configuration. In this context, it involves modifying the vertex and geometry shaders to accommodate line-based rendering, requiring changes in the input and output structures as well as possibly introducing logic to manage endpoints of lines, as lines consist of pairs of vertices instead of the triplet for triangles .

The depth (z-buffer) test is crucial for resolving pixel visibility in 3D space, ensuring that only the nearest surfaces are rendered visible by comparing depth values per pixel. In a scenario where all primitives are front-facing without overlap, sorted rendering by eye distance can eliminate the need for depth testing, as rendering order alone resolves the view correctly. However, in cases where multiple primitives potentially overlap, convergence of sorting and depth testing ensures proper rendering, as misalignment in z-depth could persist despite order, for example, intersecting geometries viewed from angles where depth misrepresentation would otherwise occur .

Eye space, also known as camera space, aligns the coordinate system with the camera's viewpoint, where the camera is considered the origin. Transformations from object space to eye space involve applying the modelview matrix, combining translation, rotation, and scaling to position objects relative to the camera's perspective, eliminating dependency on global coordinates and enabling consistent rendering across scenes. This matrix is essential in aligning objects with the camera frame, setting up a projection basis for final display .

Texture accesses in vertex shaders are inefficient due to the lack of parallel texture-fetch units dedicated for vertex processing and the relatively low computation-to-memory-fetch ratio in vertex shaders. Sampling textures is computationally expensive and can bottleneck vertex throughput due to latency, especially when vertex shaders work on individual vertices rather than the high-throughput operations in fragment shaders designed to handle such tasks efficiently .

`glStencilFunc` configures the stencil test conditions, determining if a fragment passes based on stencil value comparisons. Meanwhile, `glStencilOp` defines actions for updating stencil values based on test outcomes, thus influencing pixel writing operations. Their coupling enables complex rendering effects like masking, shadowing, and multi-pass rendering techniques by affordably controlling pixel acceptance per frame-buffer sessions .

The amount of data transferred from the CPU to the GPU can be approximated by considering each vertex's data that includes position coordinates and color values. For n tiles, each with 3 vertices (as indicated by use of triangles), the data size could be calculated by multiplying the size of each piece of vertex data (such as position vectors and colors) by the total number of vertices. Differences arise because even though the CPU sends data for each vertex, the vertex shader processes in groups for efficiency, possibly using indexed vertices that reduce redundancy, or leveraging uniform variables which are independent of vertex count .

Moving the 'flat' qualifier, which prevents interpolation across fragments, could affect rendering by causing discontinuities in resolved fragment values especially for normals and vertex IDs. Removing 'flat' from 'normal_e' and 'vertex_id' could result in incorrect shading and ID assignment as values are interpolated for non-planar fragments. Conversely, adding 'flat' to 'vertex_e' and 'tcoord' inhibits smooth transitions of texture coordinates, leading to jagged texturing effects. Correctness entails appropriate usage of 'flat' for discrete values, ensuring rendering fidelity and data integrity, while efficiency concerns arise from potential over-processing if qualifiers are misused, leading to wasted computations .

Constant variables such as `delta_theta`, which define fixed angular increments for geometric operations, improve efficiency by providing a pre-computed step value for uniform angular distribution, reducing runtime calculations. In shaders, especially for constructs like triangle fans or regularly distributed vertices for circles, such constants allow iterative constructs to generate geometry efficiently, maintaining consistent accuracy across frames and simplifying mathematical operations involved in rendering a sequence of connected vertices .

This buffer-binding and update strategy is efficient for the first frame because all data is initialized and buffered with a single update call, reducing CPU overhead and avoiding multiple OpenGL state changes. For subsequent frames, efficiency is retained by selectively updating only the data that has changed, thus minimizing data transfer between CPU and GPU. This is managed by flags indicating data staleness, allowing unchanged data to remain untransferred, reducing unnecessary bandwidth usage .

You might also like